dd
dd
Alpha release

Run Linux containers on macOS — with no VM.

dd's JIT runs your container's code natively and services its Linux syscalls in userspace — no hypervisor, no Linux kernel. It speaks the Docker Engine API, so docker just works.

Apple Silicon Docker Engine API 3 runtimes: Linux arm64 · x86-64 · macOS Installs without root

What you get

A userspace kernel, not a virtual machine

The container's compute runs as native Apple-Silicon instructions; only its syscalls are interpreted — by code that is the kernel.

Drop-in Docker

Implements the Docker Engine API. Your docker run / ps / images / build commands — and existing images — work unchanged.

Three guest runtimes

Native arm64 Linux, x86-64 Linux via the JIT, and macOS — one engine, three runtimes, no VM in any of them.

Faster than emulation

Native-speed arm64, and x86-64 that beats a VM's qemu emulation — up to 21× on floating-point. See the numbers →

Real container isolation

Overlay image layers (copy-up / whiteout), a TOCTOU-free path-jail VFS, PID/UTS/USER namespaces, a private loopback netns, port publishing, cgroup memory + pids limits.

The JIT is the kernel

Namespaces, cgroups, image layers and networking are ordinary userspace state — the gVisor / PRoot lineage, with none of a VM's cost.

Desktop app, no root

A native macOS app plus a ddcli tool install a per-user background daemon and a docker context — never sudo.

Why a JIT, not a VM

A container should be a process, not a VM

Every other way to run Linux containers on a Mac — Docker Desktop, Colima, OrbStack — boots a Linux VM under a hypervisor. That VM is a tax you pay all day. dd deletes it.

dd — userspace kernel (JIT) VM-based Docker
Resident RAM when idleNone — per-container, freed on exitGigabytes for the VM, always on
StartupProcess spawnBoot a Linux VM + the in-VM daemon
Bind-mount / file I/ODirect host filesystem via a path jailvirtiofs/FUSE bridge across the VM
Port publishingStraight to host socketsThrough the VM's NAT layer
Battery / backgroundNothing running when idleA VM idling and draining battery
ObservabilityA normal macOS processAn opaque VM

Honest trade-off: by default the guest runs in one process — fast, and great for code you trust. For untrusted code there's now an opt-in sentry split (the gVisor shape: a deny-default Seatbelt-sandboxed worker with no host authority + a trusted sentry serving syscalls over a shared-memory ring). It's early — core file syscalls today, sockets/exec/fork in progress — so a VM still exposes a narrower attack surface for fully hostile code.

Performance

Native-speed arm64. x86 that laps emulation.

The same Linux binary, run two ways on an Apple M5 Pro: inside a Linux VM (how VM-based Docker works) vs. through dd's JIT with no VM. Median of 7 — and the dd lane even pays a bridge tax the real app doesn't, so it's conservative.

≈1×
arm64 vs native

dd runs arm64 compute at native speed — and beats it on some workloads.

26×
faster x86 floating-point

vs. emulating the same x86 binary under qemu in a VM.

no VM
+ startup, RAM, I/O

structural wins on top — no machine to boot, direct host I/O.

arm64 — dd vs a native VM

WorkloadVMdddd vs VM
int sieve0.74s0.48s1.55× faster
mandelbrot0.76s0.74s1.03× faster
matrix multiply0.63s0.64s~parity
memcpy0.53s0.54s~parity
base640.65s0.65s~parity
float n-body0.16s0.17s~parity
SHA-2560.77s0.80s~parity
qsort0.79s1.05s1.33× slower
text scan0.49s0.66s1.35× slower
SQLite 600k0.35s0.52s1.48× slower

x86-64 — dd vs VM emulation (qemu)

WorkloadVMdddd vs VM
float n-body5.18s0.20s26× faster
matrix multiply8.08s0.65s12× faster
mandelbrot7.62s0.81s9.4× faster
SQLite 600k2.88s0.73s4.0× faster
qsort3.86s1.36s2.8× faster
memcpy2.30s0.92s2.5× faster
int sieve1.26s0.63s2.0× faster
text scan1.36s0.88s1.6× faster
SHA-2562.60s1.83s1.4× faster
base644.10s4.71s1.15× slower

Across 10 workloads, dd beats qemu-in-a-VM on 9 of 10 for x86, and matches a native arm64 VM on compute. Where it's behind — allocation/syscall-heavy work (arm64 SQLite, qsort) — is exactly the optimization frontier.

Compute micro-benchmarks, median of 7, same static binary timed two ways (make bench). The userspace-kernel tax shows on syscall-heavy work; a Rosetta-backed VM would narrow the x86 gap.

Get started

Install once, then it's just Docker

Download the app, drop it in Applications, run one setup command. dd registers a background daemon and a docker context — no VM, no sudo. The ddcli tool ships inside the app.

The easy way — ddcli

One word drops you into a shell in any image — current dir mounted, arch auto-detected.

# a shell in ubuntu, here in this folder
$ ddcli ubuntu

# a one-off command; force amd64 (x86 JIT)
$ ddcli run alpine echo hi
$ ddcli run ubuntu --platform linux/amd64

# a macOS container
$ ddcli mac

…or plain Docker

dd registers a dd context, so every ordinary flag flows through.

$ ddcli install   # daemon + context (no sudo)

$ docker --context dd run -p 8080:80 alpine \
      sh -c 'echo hi from $(hostname)'
$ docker --context dd pull ubuntu
$ docker --context dd ps

Download dd for macOS

Apple-Silicon Macs (arm64), macOS 12+. A single self-contained app — the UI, the ddcli tool and the runtime. Free and open source (MIT).

Download the latest .dmg

Alpha software — expect rough edges. Not notarized yet, so on first launch right-click ddOpen once; it runs normally after. ddcli doctor can clear it for you.