No Docker, no full VM — just a lightweight per-process virtual machine that translates Linux syscalls to macOS equivalents. Millisecond startup, native speed, ~170 syscalls.
A single 250KB native macOS binary. No daemon, no disk image, no multi-GB download.
VM creation takes ~1–3ms. Docker container start is seconds; full VM boot is minutes. hl is imperceptible in pipelines.
Guest aarch64 code runs on real ARM64 hardware — zero instruction emulation. x86_64 runs via Apple's Rosetta with hardware-accelerated JIT/AOT.
aarch64-linux natively, x86_64-linux via Rosetta. Both from the same tool, auto-detected from the ELF header.
Statically linked ELFs work out of the box. Dynamically linked work with --sysroot (musl or glibc).
The guest reads and writes the macOS filesystem directly. No overlay, no volume mount, no copy. Changes are instant.
Up to 64 concurrent guest threads, each mapped 1:1 to a host pthread with its own HVF vCPU. Full futex support.
I/O, filesystem, networking, signals, threading, process management, time, epoll, inotify, /proc, /dev — all translated.
A single native macOS binary. No daemon, no runtime, no disk image. Runs from anywhere — even a USB stick.
A 5-step pipeline that translates Linux syscalls to macOS equivalents inside a lightweight HVF virtual machine.
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).
A minimal EL1 kernel shim (~500 lines of AArch64 assembly) provides exception vectors. The MMU is enabled inside the guest on the first instruction.
The guest binary executes at full native hardware speed on an Apple Hypervisor.framework vCPU.
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.
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.
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 | ~0.2–4 s | ~5–30 s | ~2–60 s |
| Memory overhead | Used pages only (~MB) | VM RAM (default ~50% host) | VM RAM (default 4 GB) | VM RAM (configured at creation) |
| Disk overhead | ~250 KB binary | Multi-GB (app + VM image) | Multi-GB (VM image) | Multi-GB (app + VM image) |
| Filesystem | Direct macOS FS access | virtiofs bind mounts | virtiofs (VZ) / 9p (QEMU) | virtiofs / shared folders |
| x86_64 support | Via Rosetta (fast) | Rosetta or QEMU | Rosetta (VZ) or QEMU | Rosetta (VZ) or QEMU TCG |
| 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 |
Verified with real-world programs — from coreutils to Haskell binaries. Both aarch64 and x86_64 via Rosetta.
102 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 + dynamic60+ applets including awk, sed, grep, tar, gzip, bzip2, find, bc, diff, hexdump, wget, nc, ash shell.
staticbash (arrays, associative arrays, regex, arithmetic, subshells/fork), dash (loops, conditionals, recursion, case/glob).
static + dynamicjq (JSON processing), GNU grep, GNU sed, GNU find, tree, diffutils.
staticCREATE, INSERT, SELECT, ORDER BY, recursive CTEs, string functions. Full database engine running in the guest.
staticLua interpreter (Sieve of Eratosthenes, Fibonacci), GNU awk (Mandelbrot renderer).
staticMarkdown→HTML, HTML→Markdown, JSON AST. Exercises GHC RTS + Lua FFI.
haskell · dynamicShell script linter. Exercises GHC threaded RTS + regex engines.
haskell · dynamicAll of the above also work as x86_64-linux binaries, auto-detected from the ELF header. Some edge cases (SA_RESETHAND signals, TLS-dependent threading) have known Rosetta limitations.
rosetta · JIT + AOT
hl takes a Linux ELF binary and runs it. That's it.
$ 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
Pure computation runs at native ARM64 hardware speed. The only overhead is per-syscall VM exit round-trips.
mmap + hv_vm_create + page table setup. Imperceptible in pipelines.
Each VM exit round-trip for syscall translation. Microseconds, not milliseconds.
Guest aarch64 code runs on real ARM64 hardware. No instruction emulation, no interpretation.
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.
Apple Silicon Mac (M1+), macOS 13+. macOS 15+ recommended for 1TB address space.
One command. Downloads, codesigns, and installs to /usr/local/bin.
curl -fsSL https://hyper-linux.app/install.sh | sh
Via the zw3rk tap. Handles codesigning automatically.
brew install zw3rk/hyper-linux/hl
Run directly without installing, or add to your flake inputs.
nix run github:zw3rk/hyper-linux
Key innovations that make kernel-less Linux binary execution possible.
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.
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.
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.
Guest threads map 1:1 to host pthreads, each with its own HVF vCPU sharing the same physical memory. Full futex support (including PI) enables musl pthread and GHC RTS.
x86_64 binaries run by co-loading Apple's Rosetta at 128TB using 48-bit IPA. JIT and AOT translation with persistent caching via built-in rosettad handler.
/proc/self/exe, /proc/cpuinfo, /proc/meminfo, /proc/self/maps, /dev/null, /dev/urandom, and more — synthesized responses that match Linux format.
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)
0x7800000 Stack guard page (dynamic, moves for large binaries)
0x7801000 Stack (8MB, grows down from 0x8000000)
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)
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.
hl translates syscalls, not the kernel.
Some things are outside scope by design.
Requires M1/M2/M3/M4 or later. Intel Macs are not supported.
No namespaces, cgroups, or kernel modules. This is syscall translation, not a full kernel.
ptrace is implemented internally (used by Rosetta's JIT), but GDB/LLDB cannot attach to the guest from the host.
Up to 64 concurrent guest threads per VM instance.
Shared memory semantics are not preserved across processes (guest is process-scoped).
x86_64 binaries get JIT translation immediately and automatic AOT pre-translation via the built-in rosettad handler with persistent caching.