carrick

Run unmodified Linux binaries on macOS. No VM, no guest kernel, no daemon.

Carrick loads a Linux ELF binary, runs its ARM64 instructions directly on the CPU via Apple's Hypervisor.framework, and traps each svc #0 (Linux syscall) at the hardware boundary. A Rust runtime translates the trapped call to a Darwin equivalent and resumes the guest. The Linux process becomes a native macOS process — visible to ps, lsof, kill, and dtrace on your host.

Install

$ curl -fsSL https://carrick.sh | sh

Requires macOS 14+ on Apple Silicon.

Usage

# pull an OCI image and run a command
$ carrick run ubuntu:24.04 /bin/bash -c 'apt-get update && apt-get install -y hello && hello'
Hello, world!

# interactive shell with a real PTY
$ carrick run -t alpine:latest /bin/sh

# run a prebuilt Linux ELF directly — no image pull
$ carrick run-elf ./my-linux-binary --flag value

# bind-mount a host directory into the guest
$ carrick run -v /Users/me/data:/mnt:ro ubuntu:24.04 ls /mnt

# live syscall trace via DTrace USDT probes
$ sudo carrick trace run alpine:latest /bin/echo hi

What happens under the hood

When you run carrick run ubuntu:24.04 python3 -m http.server, here's what actually happens:

$ carrick run ubuntu:24.04 python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 ...

# In another terminal — this works because guest sockets
# bind directly to host interfaces. No port forwarding.
$ curl -s http://localhost:8000/ | head -5
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>

# The guest Python process is a real macOS process:
$ ps aux | grep python
me  41892  0.4  0.1  ... python3 -m http.server

No VM booted. No container runtime started. The Python binary's ARM64 instructions executed on your CPU at EL0 (unprivileged). When Python called bind(), carrick trapped the svc #0, decoded it as Linux sys_bind, and called macOS bind() on a real host socket. The server is listening on your actual network interface.

How it works

Carrick uses Apple's Hypervisor.framework to create a lightweight execution context — not a virtual machine. There is no guest kernel, no virtual disk, no BIOS. One host pthread and one HVF vCPU per guest thread.

  Linux Binary (ARM64 EL0)          Carrick Runtime (Rust)         macOS Kernel
 ┌──────────────────────┐        ┌──────────────────────────┐    ┌──────────────┐
 │                      │  trap  │                          │    │              │
 │  guest executes      │───────>│  VBAR_EL1 vector catches │    │              │
 │  svc #0 (syscall)    │        │  hvc #0 exits to host    │    │              │
 │                      │        │                          │    │              │
 │                      │        │  decode x8 (syscall nr)  │    │              │
 │                      │        │  decode x0-x5 (args)     │───>│  Darwin API  │
 │                      │<───────│  write result to x0      │<───│  (native)    │
 │  resumes execution   │        │  resume vCPU             │    │              │
 └──────────────────────┘        └──────────────────────────┘    └──────────────┘

What works today

Carrick runs real workloads end-to-end today — apt-get, Python servers, the Go runtime test suite. It is not a complete Linux kernel replacement; roughly 75% of syscalls are implemented. We publish conformance baselines so you can assess fit.

WorkloadStatusDetail
apt-get install verified Runs end-to-end including dpkg post-install scripts.
Python http.server verified ThreadingHTTPServer serves concurrent requests from the host. Single-digit ms response times.
Go runtime suite verified 341/341 tests pass. context 38/38, sync/atomic 95/95.
LTP syscall conformance verified 568/896 valid tests match (63%). Strong: sched 76%, timers 74%, signals 73%, fs 68%. Weaker: mm 34%, ipc 38%.
CPython module parity verified 356/482 modules match (74%). Skewed by simple modules; test_subprocess/test_multiprocessing blocked (see limitations).
Interactive shell (-t) verified Real PTY via pty_relay.rs. Ctrl-C, Ctrl-Z, job control work.
Docker-style CLI verified OCI pull, layer composition, -e/-v/-w/--entrypoint flags.

Full baseline data: compatibility page.

Performance

Measured via DTrace USDT probes on carrick run … /bin/true:

PhaseCostNote
First boot ~90 ms OCI load + hv_vm_create + page tables + ELF load
Fork ~5.7 ms HVF context rebuild — ~16× cheaper than boot, no global lock
Fork + exec ~7.8 ms vs ~1 ms native Linux fork+exec
Child teardown ~7 µs Effectively free
Process teardown ~175 ms Kernel reclaiming the large VM mapping

Known limitations

Carrick is syscall emulation, and that cuts both ways. Not every binary will work. We'd rather be upfront about this than have you find out the hard way.

Compared to VMs

Carrick is not a Docker replacement or container orchestrator. It complements containers by running individual Linux binaries without the VM.

Docker Desktop Lima / Colima Full VM Carrick
Model Linux VM → containers Linux VM Full guest OS Binary as macOS process
Startup 30–60 s 10–30 s Minutes ~90 ms / ~5.7 ms fork
Filesystem FUSE sync Mounted share Virtual disk Direct host paths
Networking Port forwarding Shared IP NAT/bridge Direct host sockets
Host integration Opaque Inside VM Inside VM ps/lsof/kill/dtrace

Links