Results
macOS Apple M5, 10 cores, 16 GB, macOS 26
Startup run --rm echo hello Wall time of a one-shot run, mean of 10 · lower is better
22 % faster
CPU sysbench cpu, 4 threads Events per second
6 % faster
Memory sysbench memory, 1K blocks 4 threads
even
Disk fio random read 4k 4 jobs × 1 GiB, 60 s
6.3× faster
Disk fio random write 4k 4 jobs × 1 GiB, 60 s
even
Disk fio sequential write + fsync 1 MiB blocks, 1000 MB
47 % slower
Network iperf3 host → VM TCP through a published port
even
Network iperf3 VM → host TCP through a published port
40 % slower
Apps Redis SET redis-benchmark through a published port
38 % faster
Apps HTTP, Go server wrk, 4 threads, 100 connections
91 % faster
Docker Desktop runs all containers inside one Linux VM; jerboa boots one VM per app on Hypervisor.framework.
Linux Xeon E5-2620 (2012), nested KVM guest with 2 vCPU and 4 GB
Startup run --rm echo hello Wall time of a one-shot run, mean of 10 · lower is better
2.1× faster
CPU sysbench cpu, 4 threads Events per second
even
Disk fio random read 4k 4 jobs × 1 GiB, 60 s
2.5× faster
Disk fio random write 4k 4 jobs × 1 GiB, 60 s
46 % faster
Disk fio sequential write + fsync 1 MiB blocks, 1000 MB
15 % slower
Apps Redis SET, client on the host Published port, kernel NAT
15 % faster
Apps Redis SET, client on the LAN Published port, --port-publish nat-lan
37 % slower
Apps HTTP, Go server wrk, 4 threads, 100 connections
7 % slower
This host is itself a VM without APICv, so every guest exit costs 50–120 µs instead of 1–2 µs. That penalizes jerboa’s VMs and not Docker’s containers.
How it was measured
- Same binaries. Each tool is built once per architecture and runs unchanged in both runtimes. jerboa imports the same Debian image Docker runs, with
jerboa pkg from-docker. - Interleaved. Rounds alternate between the runtimes and rotate their order, so heat and background load don’t favor one side. Only one runtime runs at a time.
- Medians. Every figure is the median of at least three runs. All samples are kept in the engine’s benchmark notes, under the series name in the Sources table.
- Limits. In the battery (start-up, CPU, memory, disk) Docker runs without limits, as in the original benchmark. In the network and application series its CPU and memory limits match the jerboa VM’s.
- Tooling. The test battery follows OneUptime’s container benchmark (sysbench, fio, iperf3, wrk, redis-benchmark). Network and application series use the same tools.
What is left out
macOS
- Cold start: Docker’s number includes pulling node:20-alpine, so the comparison is not like for like.
- Memory with 1 MiB blocks: with 4 threads on one shared buffer the result depends on where macOS places the vCPUs. With 1 thread both reach about 52 GB/s.
- stress-ng: it needs fork(), which a unikernel does not provide.
Linux
- sysbench memory: Docker measured 569 MiB/s with 1K blocks on this host, against 14.6 GB/s with 1 MiB blocks. That result has no explanation yet, so no comparison is published.
- Cold start: Docker’s number includes pulling node:20-alpine.
- stress-ng: it needs fork(), which a unikernel does not provide.
Sources
| Host | Test | Docker | jerboa | Unit | Series |
|---|---|---|---|---|---|
| macOS | run --rm echo hello | 131 | 107 | ms | full-1008 |
| macOS | sysbench cpu, 4 threads | 54,832 | 58,146 | ev/s | full-1008 |
| macOS | sysbench memory, 1K blocks | 5,954 | 5,693 | MiB/s | full-1008 |
| macOS | fio random read 4k | 29,746 | 187,900 | IOPS | full-1008 |
| macOS | fio random write 4k | 45,600 | 47,600 | IOPS | full-1008 |
| macOS | fio sequential write + fsync | 4,673 | 2,475 | MiB/s | full-1008 |
| macOS | iperf3 host → VM | 13 | 13 | Gbit/s | gw-ab7 |
| macOS | iperf3 VM → host | 27.7 | 16.5 | Gbit/s | gw-ab7 |
| macOS | Redis SET | 71,000 | 98,000 | req/s | gw-ab7 |
| macOS | HTTP, Go server | 40,600 | 77,700 | req/s | gw-ab7 |
| Linux | run --rm echo hello | 2,175 | 1,019 | ms | full-1008 |
| Linux | sysbench cpu, 4 threads | 1,257 | 1,204 | ev/s | full-1008 |
| Linux | fio random read 4k | 171 | 421 | IOPS | full-1008 |
| Linux | fio random write 4k | 4,611 | 6,746 | IOPS | full-1008 |
| Linux | fio sequential write + fsync | 142 | 121 | MiB/s | full-1008 |
| Linux | Redis SET, client on the host | 10,435 | 11,962 | req/s | app-ab1 |
| Linux | Redis SET, client on the LAN | 18,300 | 11,463 | req/s | lan-ab1 |
| Linux | HTTP, Go server | 6,696 | 6,220 | req/s | app-ab1 |
Run it yourself
The engine ships jerboa-bench, which packages one static test service as a jerboa image and as a FROM scratch Docker image, checks that both hold the same binary, and shuffles the runs.
shell
go build -o dist/jerboa-bench ./cmd/jerboa-bench
dist/jerboa-bench --jerboa dist/jerboa \
--runtime fc=jerboa,network=bench,layout=compact \
--runtime docker=docker \
--runs 30 --warmup 3 --out bench.json