Run Linux binaries on macOS.

No Docker, no full VM — just a lightweight per-process virtual machine that translates Linux syscalls to macOS equivalents. Millisecond startup, native speed, ~160 syscalls.

$ curl -fsSL https://hyper-linux.app/install.sh | sh click to copy
~1–3 ms VM startup time
~160 Linux syscalls
300 KB Single binary
17,600 Lines of C + asm

Everything you need, nothing you don't

A single 300KB native macOS binary. No daemon, no disk image, no multi-GB download.

Millisecond startup

VM creation takes ~1–3ms. Docker container start is seconds; full VM boot is minutes. hl is imperceptible in pipelines.

Native speed

Guest aarch64 code runs on real ARM64 hardware — zero instruction emulation. x86_64 runs via Apple's Rosetta with hardware-accelerated JIT/AOT.

Both architectures

aarch64-linux natively, x86_64-linux via Rosetta. Both from the same tool, auto-detected from the ELF header.

Static + dynamic binaries

Statically linked ELFs work out of the box. Dynamically linked work with --sysroot (musl or glibc).

Direct filesystem access

The guest reads and writes the macOS filesystem directly. No overlay, no volume mount, no copy. Changes are instant.

Real threading

Up to 64 concurrent guest threads, each mapped 1:1 to a host pthread with its own HVF vCPU. Full futex support.

~160 Linux syscalls

I/O, filesystem, networking, signals, threading, process management, time, epoll, inotify, /proc, /dev — all translated.

Zero dependencies

A single native macOS binary. No daemon, no runtime, no disk image. Runs from anywhere — even a USB stick.

How it works

A 5-step pipeline that translates Linux syscalls to macOS equivalents inside a lightweight HVF virtual machine.

1

Load

hl parses the ELF binary and maps PT_LOAD segments into a demand-paged guest address space (up to 1TB — only touched pages use RAM).

2

Boot

A minimal EL1 kernel shim (~500 lines of AArch64 assembly) provides exception vectors. The MMU is enabled inside the guest on the first instruction.

3

Run

The guest binary executes at full native hardware speed on an Apple Hypervisor.framework vCPU.

4

Translate

When the guest makes a Linux syscall (SVC #0), the shim forwards it via HVC. hl translates it to the equivalent macOS call — errno codes, flag values, struct layouts, all converted.

5

Return

Results flow back to the guest. The vCPU resumes. This loop repeats for every syscall — typically microseconds per round-trip.

For x86_64 binaries: Apple's Rosetta Linux translator is loaded as a co-resident aarch64 binary. Rosetta JIT/AOT-translates x86_64 instructions to ARM64 at runtime. All resulting syscalls appear as ARM64 SVC #0 — hl's existing syscall handlers work transparently.

How it compares

hl is not a replacement for Docker or a full VM. It runs single Linux binaries with minimal overhead.

hyper-linux Docker Desktop Lima UTM / QEMU
What runs Single Linux binary Full Linux VM + containers Full Linux VM + containerd Full Linux/other VM
Linux kernel None (syscall translation) Yes (in VM) Yes (in VM) Yes (in VM)
Startup time ~1–3 ms ~2–10 s ~5–30 s ~10–60 s
Memory overhead Used pages only (~MB) Fixed VM RAM (2–8 GB) Fixed VM RAM (2–4 GB) Fixed VM RAM
Disk overhead ~300 KB binary Multi-GB image Multi-GB image Multi-GB image
Filesystem Direct macOS FS access Volume mount / virtiofs virtiofs / sshfs Shared folders
x86_64 support Via Rosetta (fast) Via QEMU (slow) Via QEMU (slow) Via QEMU (slow)
Threading 1:1 HVF vCPUs (up to 64) Full kernel scheduler Full kernel scheduler Full kernel scheduler
Networking In-process translation Virtual network Virtual network Virtual network
Install Single binary or nix run Desktop app brew install Desktop app

What it runs

Verified with real-world programs — from coreutils to Haskell binaries. Both aarch64 and x86_64 via Rosetta.

GNU Coreutils 9.9

104 tools tested: ls, cat, sort, grep, wc, cp, mv, rm, chmod, date, head, tail, tr, cut, uniq, seq, stat, du, df, and many more.

static + dynamic

BusyBox 1.37.0

60+ applets including awk, sed, grep, tar, gzip, bzip2, find, bc, diff, hexdump, wget, nc, ash shell.

static

Shells

bash (arrays, associative arrays, regex, arithmetic, subshells/fork), dash (loops, conditionals, recursion, case/glob).

static + dynamic

Developer tools

jq (JSON processing), GNU grep, GNU sed, GNU find, tree, diffutils.

static

SQLite3

CREATE, INSERT, SELECT, ORDER BY, recursive CTEs, string functions. Full database engine running in the guest.

static

Languages

Lua interpreter (Sieve of Eratosthenes, Fibonacci), GNU awk (Mandelbrot renderer).

static

pandoc

Markdown→HTML, HTML→Markdown, JSON AST. Exercises GHC RTS + Lua FFI.

haskell · dynamic

ShellCheck

Shell script linter. Exercises GHC threaded RTS + regex engines.

haskell · dynamic

x86_64 via Rosetta

All of the above also work as x86_64-linux binaries, auto-detected from the ELF header.

rosetta · JIT + AOT

Quick start

hl takes a Linux ELF binary and runs it. That's it.

terminal
$ hl ./hello-static
Hello, world!

$ hl /path/to/jq '.name' data.json
"hyper-linux"

# Run bash interactively with a musl sysroot
$ hl --sysroot ./musl-sysroot /path/to/bash
bash-5.2$

# x86_64 binaries are auto-detected via ELF header
$ hl ./my-x86_64-binary

# Verbose output for debugging
$ hl --verbose ./my-program arg1 arg2

Overhead characteristics

Pure computation runs at native ARM64 hardware speed. The only overhead is per-syscall VM exit round-trips.

~1–3 ms

VM startup

mmap + hv_vm_create + page table setup. Imperceptible in pipelines.

~1–5 µs

Per-syscall overhead

Each VM exit round-trip for syscall translation. Microseconds, not milliseconds.

Zero

Computation overhead

Guest aarch64 code runs on real ARM64 hardware. No instruction emulation, no interpretation.

Demand-paged

Memory usage

Address space up to 1TB, but only touched pages consume physical RAM. macOS handles the rest.

In practice: Single-command tools (echo, true, basename) are dominated by 1–3ms startup. Long-running programs (bash sessions, pandoc) amortize startup to zero — steady-state performance is native + microseconds per syscall.

Installation

Apple Silicon Mac (M1+), macOS 13+. macOS 15+ recommended for 1TB address space.

Quick install

One command. Downloads, codesigns, and installs to /usr/local/bin.

curl -fsSL https://hyper-linux.app/install.sh | sh

Homebrew

Via the zw3rk tap. Handles codesigning automatically.

brew install zw3rk/hyper-linux/hl

Nix

Run directly without installing, or add to your flake inputs.

nix run github:zw3rk/hyper-linux

macOS Installer

Download .pkg from GitHub Releases. Double-click to install.

Download v0.1.1

Technical architecture

Key innovations that make kernel-less Linux binary execution possible.

Syscall translation, not emulation

Every Linux syscall is intercepted via HVC and translated to the equivalent macOS call in-process. Full translation of errno codes, flag values, struct layouts, and socket address formats.

W^X demand paging

Apple HVF enforces write-xor-execute even with SCTLR.WXN=0. The page table system handles this via 4KB L3 splitting when mixed permissions are needed within a 2MB block.

Fork via IPC serialization

HVF allows only one VM per process. Fork spawns a new hl process and serializes entire VM state — registers, memory, FDs via SCM_RIGHTS, signals — over a Unix socket.

Multi-vCPU threading

Guest threads map 1:1 to host pthreads, each with its own HVF vCPU sharing the same physical memory. 7-operation futex enables musl pthread support.

Rosetta integration

x86_64 binaries run by co-loading Apple's Rosetta at 128TB using 48-bit IPA. AOT translation via rosettad supported for complex binaries with indirect calls.

/proc & /dev emulation

/proc/self/exe, /proc/cpuinfo, /proc/meminfo, /proc/self/maps, /dev/null, /dev/urandom, and more — synthesized responses that match Linux format.

Guest memory layout
0x010000      Page table pool (960KB)
0x100000      Shim code (2MB, RX)
0x200000      Shim data/stack (2MB, RW)
0x400000      ELF LOAD segments
0x1000000     brk heap (grows up)
0x7E00000     Stack guard page
0x7E01000     Stack (2MB, grows down)
0x10000000    mmap RX region (256MB initial, extends on demand)
0x200000000   mmap RW region (8GB, 256MB initial, extends on demand)
interp_base   Dynamic linker (60GB / 1020GB)

CLI usage

hl --help
USAGE:
    hl [OPTIONS] [--] <elf-path> [args...]

OPTIONS:
    -h, --help              Display the manual page
    -V, --version           Print version and exit
    -v, --verbose           Enable verbose diagnostic output on stderr
    -t, --timeout SECONDS   Per-iteration vCPU timeout (default: 10s)
    --sysroot PATH          Musl/glibc sysroot for dynamically-linked binaries
    --                      Stop processing hl options

EXIT STATUS:
    The exit status of hl is the exit status of the guest binary.

ENVIRONMENT:
    Guest inherits the host environment verbatim. No special variables.

Known limitations

hl translates syscalls, not the kernel. Some things are outside scope by design.

Apple Silicon only

Requires M1/M2/M3/M4 or later. Intel Macs are not supported.

No Linux kernel features

No namespaces, cgroups, or kernel modules. This is syscall translation, not a full kernel.

No ptrace / debuggers

GDB/LLDB cannot attach to the guest process.

64 thread limit

Up to 64 concurrent guest threads per VM instance.

MAP_SHARED as MAP_PRIVATE

Shared memory semantics are not preserved across processes (guest is process-scoped).

Some x86_64 binaries need AOT

Complex x86_64 binaries with indirect calls may need rosettad AOT translation.