The best container runtime for a Mac, benchmarked.
A Linux container on a Mac runs inside a Linux virtual machine, and the runtime is the layer between that machine and yours. Most teams inherit one and never revisit it. This is a measured comparison of the five current options, Docker Desktop,OrbStack, Apple's container,Podman and Colima, on an M2 Pro, against a workload shaped like a Python data platform: a scheduler, a time-series database, a relational database, a broker, and images built from a lockfile with sixty scientific packages.
None is fastest in general. Each is fastest at one boundary, and the ranking flips depending on whether the work crosses the host filesystem, a database's write path, the network, or an emulated CPU. Each runtime keeps the same colour and position in every chart and table.
OrbStackDocker DesktopApple containerColimaPodman
The trade that decides itup and left is better
x: seconds to unpack 200,000 files onto a host bind mount (log). y: pgbench transactions per second. Nobody wins both.
If the day is dominated by
Pick
Because
A source tree mounted into containers
OrbStack
3 to 4x faster at that boundary, fastest to start, best under memory pressure, joint top on compatibility, $8 per user per month against $24 for Docker Business
A local database
Docker Desktop
twice OrbStack's write throughput on volumes, fastest rebuilds and container starts
x86 images, rootless, or no budget
Colima or Podman
free; set --vz-rosetta at creation on Colima; neither mounts its socket into a container
A few containers in strong isolation
Apple container
best emulated x86, smallest idle footprint; not ready to standardise on: no Compose, no Engine API, per-container memory and network costs
Rancher Desktop (installer conflicts with Docker Desktop; same Lima family as Colima) and Finch (needs interactive sudo; same family)
Runtime
Version
Architecture
OrbStack
2.2.3, engine 29.4.0
one lightweight VM, its own filesystem bridge
Docker Desktop
4.90.0, engine 29.7.2
one VM, moby, VirtioFS
Apple container
1.3.1
one micro-VM per container, vmnet, an IP each
Colima
0.10.3, Lima 2.2.0
one VM (vz), dockerd, virtiofs
Podman
6.1.1
one VM (applehv), crun, rootless
Method
Isolation
Exactly one runtime alive at a time, verified against the hypervisor processes. Every other runtime stopped before each block.
Repeats
Core suite: 3 rounds, runtime order reshuffled each round from a fixed seed; 12 repeats inside the container-start metric. Application, stress, fan-out and compatibility passes: once each; where they produce a rate, the rate is compared.
Statistics
Medians. Three-round metrics show min and max beneath the median in tables and as whiskers in charts. Failures are reported with their error, never imputed; a throughput of exactly zero is treated as a failure to investigate.
Drift control
A fixed single-core host task ran before and after every block. Baselines across all blocks: 0.612 to 0.644 s. Drift within a block: -6.2% to +1.5%. No block was discounted for drift.
Cold state
Image stores purged before each pull measurement and at the end of each block. 20 s thermal settle before each block.
Load evidence
A host sampler recorded CPU, memory, swap and disk every 2 s during the heavy passes: CPU peaked at 68 to 100%, memory at 16 to 20 GB of 32, swap at 2.7 GB on every runtime.
Pre-registration
Ten hypotheses written before any data. Scored at the end, including the one that failed.
Data
About 750 raw measurements across nine passes; 17 logged deviations, the material ones listed at the end.
Start and pull
Apple's runtime is ready fastest and starts each container slowest, because each one is a VM. Its image pulls are six times slower than the others for the same four images.
OrbStack
Docker Desktop
Apple container
Colima
Podman
Cold start to first container s
1.251.14-2.65
4.162.77-12.3
1.661.35-1.88
13.913.4-14.5
10.810.7-10.9
Warm container start, p50 of 12 s
0.280.27-0.28
0.130.13-0.14
1.020.91-1.12
0.170.17-0.18
0.190.18-0.19
Pull four images s
20.320.2-20.8
18.618.3-44.7
132131-180
21.821.1-23.5
29.025.1-40.6
Compute
Under seven per cent spread on every compute metric. All five sit on the same hypervisor and the same silicon; work that stays inside the container does not see the runtime.
OrbStack
Docker Desktop
Apple container
Colima
Podman
Single-core throughput MiB/s
25282508-2554
24922467-2498
25562555-2557
24432372-2478
24442384-2523
Six-core throughput MiB/s
12,97512,646-13,125
12,80612,555-12,815
13,02912,903-13,032
12,19410,703-12,605
12,48811,559-12,849
Airflow, 8M rows, arm64 rows/s
469,491
468,832
485,682
463,919
474,790
The host filesystem boundary
The same untar, checksum and delete cycle on a host bind mount, where a mounted source tree lives, and on a native volume, which never leaves the VM. Bind-mount times differ four times across runtimes; volume times differ two, and the order inverts. The lines below are near-parallel, so the gap is a constant factor, not a scaling difference.
Seconds to unpack a source treesolid bind mount · dashed volume
OrbStack
Docker Desktop
Apple container
Colima
Podman
Untar 20k files, bind mount s
2.252.22-2.36
6.906.83-6.92
8.378.37-8.39
9.168.87-9.74
9.619.48-10.1
Checksum 20k files, bind mount s
0.580.56-0.58
2.011.98-2.03
2.782.76-2.80
3.543.40-3.80
4.264.04-4.43
Untar 20k files, volume s
0.160.15-0.16
0.210.21-0.22
0.150.15-0.17
0.230.23-0.24
0.290.26-0.30
Untar 200k files, bind mount s
23.5
67.8
86.9
92.5
93.4
Checksum 200k files, bind mount s
6.80
25.7
39.1
39.8
48.3
Untar 200k files, volume s
8.99
2.26
1.77
2.69
3.53
Random 4 KiB writes, bind mount IOPS
48,677
23,545
11,239
10,259
10,432
Random 4 KiB writes, volume IOPS
17,854
36,914
43,728
41,238
39,515
Small-file ops under full contention, bind mount ops/s
14,470
1120
1437
2402
1824
Build
Cold builds differ by a factor of two. Rebuilding after a one-line source change differs by nearly twelve, and that is the operation paid dozens of times a day.
Seconds to build the 60-package imagelog scale
OrbStack
Docker Desktop
Apple container
Colima
Podman
Cold build, 60 packages s
51.548.9-53.3
35.735.3-113
59.154.4-67.3
46.846.1-47.2
71.770.2-82.4
Rebuild after a source change s
2.442.38-2.51
0.740.73-4.77
8.618.16-11.9
1.401.37-1.52
8.347.22-11.7
Four cold builds at once s
60.2
39.7
77.1
60.0
137
Network
One stream flatters the published-port path; sixteen at once show it under load. Docker Desktop moves 2,057 MiB/s on one stream and 152 across sixteen. Apple's container-to-container figure is low because that traffic crosses a virtual network between two VMs rather than staying inside one kernel.
Host to container, MiB/stinted 1 stream · solid 16 streams
OrbStack
Docker Desktop
Apple container
Colima
Podman
Host to container, 1 stream MiB/s
47084403-4764
20572048-2058
648648-649
297282-306
270235-270
Host to container, 16 streams MiB/s
7646
152
4602
301
273
Container to container MiB/s
11,54011,466-11,915
11,1899222-12,518
1599
15,02714,183-15,449
17,35515,892-17,502
New connections per second conn/s
4084
2024
2865
1526
1654
Round trip, p99 ms
0.34
0.59
0.56
1.08
0.57
Under heavy load
pgbench at scale 150 (2.2 GB) with 32 clients for two minutes; five minutes of simultaneous CPU, small-file and block-I/O contention; three containers each holding a 4 GiB working set against an 8 GB ceiling; up to fifty containers at once. OrbStack is three to four times faster on the host filesystem and posts half Docker Desktop's database throughput, which matches its random-write figure on volumes. A source tree and a database sit on opposite sides of the same trade.
OrbStack
Docker Desktop
Apple container
Colima
Podman
pgbench, 32 clients tps
7874
16,460
5205
15,164
13,582
Containers surviving 12 GiB against 8 GB of 3
3
2
3
1
1
Time to start 20 containers s
1.70
0.74
7.54
0.86
1.31
Four-service stack ready s
2.54
2.66
5.10
3.05
3.07
Seconds to start N idle containerslog-log
A VM per container is a straight line with a steeper intercept: Apple takes 40 s to reach fifty, the shared-VM runtimes 4 to 10 s.
Airflow and InfluxDB, arm64 and amd64
Airflow 3.3.1 running a three-task pipeline that moves eight million rows through SQLite. InfluxDB 2.9.1 under four concurrent writers pushing ten million points, then four full-range Flux queries. Both repeated with the whole stack emulated onlinux/amd64, at 2M rows and 1M points, compared as rates. Natively the runtime barely matters; emulated, it decides everything.
InfluxDB v2, four writers · points per secondtinted native arm64 · solid emulated amd64
Native bars sit within a few per cent of each other. The emulated bars are the story, and the number after each is how many times slower than native.
OrbStack
Docker Desktop
Apple container
Colima
Podman
InfluxDB v2, 4 writers, arm64 points/s
1,049,591
1,195,788
893,516
1,130,755
1,220,473
InfluxDB v2, 4 writers, emulated amd64 points/s
135,184
182,102
200,337
67,792
44,345
InfluxDB, emulated vs native x slower
7.76
6.57
4.46
16.7
27.5
Airflow 8M-row pipeline, arm64 rows/s
469,491
468,832
485,682
463,919
474,790
Airflow 2M-row pipeline, emulated amd64 rows/s
164,390
166,529
169,369
28,059
20,721
Airflow, emulated vs native x slower
2.86
2.82
2.87
16.5
22.9
The Rosetta flag
On a synthetic CPU test Colima's emulation penalty was 63.82x against 5.4 to 10.3 for the others. Rosetta is off by default in Colima and is requested when the VM is created. Rebuilt with --vz-rosetta the penalty fell to5.22x, the lowest of any runtime. A creation-time flag is one most teams will not pass. Native beats emulated by enough everywhere that building arm64 images for local work is worth doing whatever production runs on.
OrbStack
Docker Desktop
Apple container
Colima
Podman
Colima + Rosetta
CPU, emulated vs native x slower
6.66
5.38
5.37
63.8
10.3
5.22
Disk
Measured with each runtime stopped, so shutdown compaction is counted. Three of five return space when images are deleted. Apple stores images as expanded per-image filesystems, so its loaded footprint is large, and all of it comes back. Colima's purge left ten images behind, so its figures are an upper bound.
Runtime
Empty
Four images
After deleting them
Returns space
OrbStack
0.06 GB
1.38 GB
0.06 GB
yes
Docker Desktop
3.73 GB
4.72 GB
3.73 GB
yes
Apple container
1.81 GB
39.36 GB
1.81 GB
yes
Colima
13.4 GB
14.73 GB
14.73 GB
no
Podman
56.23 GB
56.23 GB
56.23 GB
no
Compatibility
Ten features a typical project depends on. The gaps stop work rather than slow it: no Engine API means socket-driven tooling cannot see the runtime; no Compose means the stack file does not run; no socket mount means Testcontainers does not run. Podman's Compose failure is one service, RabbitMQ, which took the stack down with it. Started by hand with plain run commands, every runtime brought the same four services up in 2.5 to 5.1 s.
OrbStack
Docker Desktop
Apple container
Colima
Podman
Docker Engine API
yes
yes
no
yes
yes
Host bind mount
yes
yes
yes
yes
yes
Named volume
yes
yes
yes
yes
yes
BuildKit cache mount
yes
yes
yes
yes
yes
Multi-stage --target
yes
yes
yes
yes
yes
Published port
yes
yes
yes
yes
yes
Container-name DNS
yes
yes
admin only
yes
yes
Compose with healthchecks
yes
yes
no
yes
RabbitMQ fails
amd64 emulation
yes
yes
yes
yes
yes
Socket mount (Testcontainers)
yes
yes
no
no
no
Hypotheses
Written before the data existed; scored against it afterwards.
The host filesystem boundary dominates the dev loop
supported
bind mount 14 to 40 times slower than a volume
H3
Bind-mount implementations differ more than volume implementations
supported
4.3x spread on bind mounts, 1.9x on volumes
H4
Apple trades per-container cost for isolation
supported
slowest start, best single core, 327 MB per container
H5
Memory per container rises with a VM per container
partial
Apple 327 MB each; the others sit below the method’s noise
H6
Pull speed is implementation-bound, not network-bound
supported
132 s against 19 to 29 s for identical images
H7
x86 emulation is a large, runtime-dependent penalty
supported
2.8x to 27.5x; one runtime moves 12x on a flag
H8
Compatibility, not speed, decides adoption
supported
Apple 6 of 10 features; Podman loses Compose to one service
H9
Sparse VM disk images never return space
refuted
three of five returned it in full
H10
The inner loop differs more than the cold build
supported
11.6x spread on rebuild, 2.0x on cold build
What went wrong
Each of these produced a clean, plausible, wrong number, and none announced itself. They were caught by re-checking the surprising results by hand. The full list of seventeen deviations is in the protocol file next to the raw data; these are the ones that changed a result.
Fault
Effect
Resolution
Podman readiness check
queried a field that does not exist in its output; three rounds timed out
fixed, re-run
Apple address lookup
read the wrong JSON level and fell back to a name it cannot resolve; container-to-container and InfluxDB recorded zero
fixed, re-run
Stack test network
services on the default bridge, which resolves no names; timed out identically on all five
fixed, re-run
Registry outage
VPN resolver returned dead addresses for the registry; one round lost
re-run as round 4
Concurrent passes
five queued passes started at once and ran against different runtimes together
discarded, rebuilt sequentially
Disk method
du on a sparse VM image cannot see image growth
replaced with a stopped-VM measurement
Build-secret check
never passed --secret, so every runtime failed a feature all of them support
row removed from the matrix
Colima without Rosetta
a 63.8x emulation penalty that is a creation-time flag, not a defect
re-tested with the flag: 5.2x
The right question is not which runtime is fastest but which boundary the day's work crosses. For a source tree mounted into containers it is OrbStack by a wide margin; for a local database it is Docker Desktop by a similar one; for x86 images on a budget it is Colima with one flag set. Apple's runtime is the most interesting of the five and the least ready.