Show HN: Kern – container and resource runtime in a 1.5 MB binary, no daemon

Aug 25, 2026 01:24 AM - 2 hours ago 1

kern

kern: A fast, rootless sandbox and virtual assets runtime for immoderate workload, including untrusted and AI-generated code.

A real, kernel-enforced instrumentality successful ~3.5 ms, retired of 1 1.52 MB binary pinch nary daemon.

 'kern container app --image alpine -- echo hullo from a existent container' prints the greeting, past reports that kern started successful 3.5 sclerosis against docker run's 297 ms. A existent OCI image, rootless, a 1.52 MB binary, nary daemon, connected an Intel i7-14700KF, Linux 7.0.

0 RAM astatine rest · nary daemon, nary socket, thing to commencement · 1 fixed binary, libc its only Rust dependency

CI  Apache-2.0 Platforms

# instal the merchandise binary (static, 1.52 MB, checksum-verified by the script) curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh # a throwaway ammunition successful a existent OCI image: rootless, kernel-enforced, a fewer ms kern container dev --image alpine -it -- sh

No autochthonal Windows: usage WSL2. Install.


One binary that manages resources, of which isolation is the first. That is why location is no single statement for kern successful a comparison table: it is simply a instrumentality runtime, a sandbox, a assets slicer and a stack runner astatine once, successful 1.52 MB pinch nary daemon.

  • A existent container. Real OCI images: pull, build from a Dockerfile, commit, push, save/load. A container from an image starts successful ~3.5 ms.
  • A sandbox, ever rootless. User, PID, mount, network, UTS and IPC namespaces, an overlay or read-only guidelines pivoted in, a deny-by-default seccomp allowlist and cgroup v2 limits. One flag, --security-profile untrusted, is the full hardened bundle.
  • Resource profiles, not conscionable isolation. CPU (vcpu:), memory, disk (vdisk:) and devices (vgpio:), declared erstwhile successful a kern.toml and attached by name. kern tally applies the aforesaid caps to a process connected the host, pinch nary sandbox astatine all. docs/RESOURCES.md
  • Stacks, successful kern's ain format aliases successful Docker's. kern constitute <file> up takes a kern-compose.toml ([box.NAME] tables, pinch the assets profiles above) aliases the docker-compose.yml you already have, read arsenic written. One stack to 1 pod, services reaching each different by name.
  • The devices astir them. ps, logs, exec, stats, inspect, wait, apical (a unrecorded TUI), doctor, positive a Python and Node SDK and an MCP server for agents.

Its full Rust dependency character is libc: JSON and OCI manifests are parsed by hand, and pull shells retired to the curl and tar already connected the instrumentality alternatively than linking a TLS stack. (1.52 MB is the size-optimized merchandise build; a plain cargo instal from root is 1.91 MB.)

 a kern.toml defines reusable vcpu/vdisk/vgpio (device) profiles; 'kern container train --image alpine vcpu:heavy vdisk:scratch' attaches a 4-vCPU, 8 GB, 2 GB-scratch rootless isolated portion successful a fewer sclerosis (docker tally takes ~297 ms); 'kern tally vcpu:heavy -- ffmpeg' caps a dense transcode pinch nary sandbox; 'kern container iot --image alpine vgpio:sensor' exposes only /dev/i2c-1 and thing else; piping a petition into 'kern container fn --image python' runs it successful a caller isolated container per petition (serverless style); 'kern constitute stack.toml up' brings up a multi-box stack; 'kern top' is the unrecorded TUI for boxes, profiles and volumes: CPU, memory, disk and devices, sliced per box, successful 1 1.52 MB fixed binary, nary daemon.

  • Not a hypervisor. The bound is the Linux kernel, truthful a kernel privilege-escalation bug is an escape. Docker and Podman stock that condition, which is why gVisor and Firecracker exist.

    Read pinch the tagline, that is 1 statement seen from some sides: untrusted and AI-generated codification is what kern is FOR, because you chose to tally it and ain the blast radius (agent tool-calls, CI jobs, build steps, codification cells). What it is not for is dispute codification from strangers, multi-tenant, connected a kernel you service different tenants from. kern does commencement rootless always, wherever Docker's is opt-in.

  • Not free of the userns trade. Its isolation is built connected an unprivileged personification namespace, a fertile root of kernel LPE bugs. SECURITY.md states this earlier immoderate claim.

  • Not a wall astir what you equine in. -v $HOME:/host gives the container your location directory: a mount is simply a spot determination you make, not a bound kern enforces. --net big and --privileged are opt-outs by name. (The 1 way kern refuses to hindrance is its ain runtime registry.)

  • Not a Docker Engine reimplementation. It speaks Docker's formats, not its API: nary overlay networks, nary plugins, nary Swarm. Matrix: docs/DOCKER-COMPAT.md.

  • Not a Kubernetes runtime. No CRI. Use containerd aliases CRI-O.

  • Not shipping GPU slices. On the roadmap, pinch nary GPU codification successful this edition, so there is thing present to spot aliases to onslaught yet.

What it does not cognize aliases does not do yet is successful OPEN_ITEMS.md alternatively than near for you to find.

kern needs a Linux kernel pinch unprivileged personification namespaces and cgroup v2. It runs connected Linux, WSL2 and ARM boards (Raspberry Pi · Jetson · Arduino UNO Q); location is no autochthonal Windows build, use WSL2 (kern ships a pre-baked WSL rootfs).

The quickest way is the merchandise binary: 1 fixed file, nary toolchain, and the book verifies its SHA256 earlier installing it.

curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh

It picks x86_64 aliases aarch64 for you, installs to ~/.local/bin (/usr/local/bin arsenic root, or KERN_INSTALL_DIR), and refuses to instal a download whose checksum does not match. Verifying by hand alternatively is 2 lines:

curl -fsSLO https://github.com/getkern/kern/releases/latest/download/kern-x86_64-unknown-linux-musl.tar.gz{,.sha256} sha256sum -c kern-x86_64-unknown-linux-musl.tar.gz.sha256 && tar xzf kern-x86_64-unknown-linux-musl.tar.gz

From source is the different route, and the full dependency character is 1 crate (libc), truthful it is short: clone, build and instal took 36 s connected a desktop (i7-14700KF), longer connected a mini ARM board.

# if you do not person Rust yet curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh cargo instal --git https://github.com/getkern/kern getkern --locked

That puts kern successful ~/.cargo/bin, which rustup adds to your PATH (open a caller shell, or source "$HOME/.cargo/env", if kern is not found).

The merchandise besides ships an aarch64 binary, a Windows .exe shim and a pre-baked WSL rootfs, each with its ain .sha256; the tag is GPG-signed and independently timestamped (provenance/).

kern expert tells you whether boxes will tally present earlier you try. Boards, WSL2 and the agelong form: docs/INSTALL.md. Common questions (Docker, bubblewrap, youki, E2B, Windows, the threat model): docs/FAQ.md.

kern container dev --image alpine -it -- sh # a throwaway ammunition successful a existent OCI image kern tally --memory 256M --cpus 0.5 -- ./crunch # headdress a process, nary sandbox kern container svc --image nginx:alpine -d -p 8080:80 \ # a service: published, restarted, health-checked --restart --health-cmd 'wget -qO- localhost:80' -- nginx -g 'daemon off;' kern ps # what is running, pinch PORTS and HEALTH kern exec svc -it -- sh # ammunition into it kern extremity svc # its signal, its grace, past the codification it exited with kern apical # unrecorded TUI: boxes, CPU/RAM, profiles, volumes kern constitute stack.toml up # a multi-box stack (examples/) aliases a compose.yml kern constitute stack.toml down # and return it down again

Untrusted code, 1 emblem for the bundle:

kern container occupation --image python:3.12-slim --security-profile untrusted --memory 256m \ -v ./job:/w -- python3 /w/x.py

--security-profile untrusted is the seccomp allowlist + --cap-drop ALL + --read-only successful one opt-in emblem (spell them retired by manus if you prefer); adhd --require-limits to garbage to commencement unless the memory/pids caps are really enforced. No web unless you ask, vulnerable capabilities dropped, seccomp ever on. Ninety runnable examples, each doing 1 thing: examples/.

Every publication verb besides answers successful JSON, truthful thing has to parse a table:

kern ps --json | jq '.[] | select(.health == "unhealthy") | .name' kern measurement ls --json # ps · images · stats · inspect · builds · pod ls · config database · diff

Your Docker Compose stack, without Docker Desktop

kern speaks docker-compose.yml. Point it astatine the stack you already person and kern constitute up runs it with nary daemon and nary Docker Desktop, the aforesaid connected Linux, WSL2 and ARM boards.

# compose.yaml - a existent stack, unchanged services: db: image: postgres:alpine environment: { POSTGRES_PASSWORD: secret, POSTGRES_DB: app } web: image: adminer ports: ["8080:8080"] depends_on: [db]
kern constitute compose.yaml up

Both charismatic images start, web reaches db by work name, and the larboard is published to the host. Warm (images cached) the web tier serves successful ~0.3 s, and the stack costs only what postgres and adminer actually usage (~66 MB here) pinch zero daemon connected top, wherever Docker Desktop is simply a inheritance VM earlier your first container.

Official images that driblet to a non-root personification (postgres, redis, ...) want uidmap and a /etc/subuid line, and outbound image pulls want pasta; some are 1 apt instal connected a dev box, and kern doctor names either if it is missing. This is the section dev loop, not a accumulation orchestrator: nary Swarm, no overlay networks.

Run supplier aliases LLM-generated codification from your ain programme with kern-sandbox, a thin, dependency-free wrapper complete the kern binary. Every telephone runs successful a caller isolated box: web off, representation and pid caps, capabilities dropped, output bounded, and a timeout the binding itself enforces.

pip instal kern-sandbox # PyPI · needs the `kern` binary above, connected PATH aliases $KERN_BIN npm instal kern-sandbox # npm · same
from kern_sandbox import run_code r = run_code("import platform; print(platform.python_version())") print(r.stdout) # ran successful a caller box; a timeout / OOM / blocked flight is information connected r.fault
  • Faults are data, not exceptions: a timeout, OOM-kill aliases blocked syscall is simply a section connected the result, not a raise. A caller container per telephone by default; Sandbox keeps a workspace crossed calls and a lukewarm kernel() keeps 1 expert for sub-millisecond cells (weaker isolation, by choice).
  • Rich results without a Jupyter kernel: the past expression, display() and matplotlib figures come backmost captured, for illustration a notebook cell.
  • Ships an MCP server (kern-mcp): a dependency-free stdio server that gives Claude Desktop, Cursor aliases immoderate MCP customer a section codification interpreter. Point the customer astatine it:
{ "mcpServers": { "kern": { "command": "kern-mcp" } } }

Tools: run_code (python/bash/node), write_file, read_file, list_files. Each telephone is simply a fresh network-off box; files persist crossed calls successful a workspace connected disk. Setup command, image and the other options: bindings/python/README.md.

Full API, Python and Node: bindings/python/README.md · bindings/node/README.md.

A portion is declared erstwhile successful ~/.config/kern/kern.toml and attached by name, to a sandboxed container aliases a bare process, pinch the aforesaid token.

Three kinds: vcpu: (CPU and memory), vdisk: (a size-capped scratch disk) and vgpio: (device nodes). Two of them, and the anchors they are carved from:

[[cpu]] # the big fund a portion is carved from id = "cpu:0" cores = 8.0 [[vcpu]] # 1.5 cores and 512 MiB -> connect arsenic vcpu:heavy name = "heavy" backend = "cpu:0" cpus = 1.5 memory = "512m" [[gpio]] # a controller anchor id = "gpio:0" [[vgpio]] # precisely 1 instrumentality node -> connect arsenic vgpio:sensor name = "sensor" backend = "gpio:0" i2c = ["/dev/i2c-1"]
kern validate ~/.config/kern/kern.toml # cheque it earlier thing runs kern container train --image alpine vcpu:heavy vdisk:scratch -- ./train.sh kern tally vcpu:heavy -- ./train.sh # the aforesaid slice, nary sandbox kern container iot --image alpine vgpio:sensor -- ls /dev

Profiles compose: respective connect to 1 box, and an definitive emblem thumps a profile's ain value. Every key is spelled for illustration its CLI flag, truthful cpus is --cpus and representation is --memory. A backend naming no declared excavation is refused erstwhile the config is read, not erstwhile the container runs. docs/RESOURCES.md has the field-by-field schema.

A vdisk: is simply a RAM-backed tmpfs erstwhile kern runs rootless, immoderate its backend says, and an ext4-on-loop image pinch a existent quota erstwhile it runs privileged. kern says which 1 you got, per profile, alternatively than letting you assume, and the size headdress is enforced either way.

vgpio: is chip-granular, not per-line. Asking for pins binds the full /dev/gpiochipN, and that characteristic instrumentality exposes each statement of that controller. pins = [17] does not restrict the box to statement 17: the kernel has nary per-line equine boundary, truthful the pin database is cooperative metadata rather than a boundary. Naming a instrumentality node, arsenic i2c supra does, grants that node and thing else.

kern Docker Podman
Daemon no yes (dockerd + containerd) no
Rootless yes, always opt-in yes
Cold start, bare box ~2.3 ms ~297 ms ~293 ms
Cold start, from an OCI image ~3.5 ms ~297 ms ~293 ms
Stop a work (init handles SIGTERM) ~1.9 ms ~310 ms ~380 ms
Resident memory, thing running 0 154 to 160 MB 0
Footprint one 1.52 MB binary daemon stack multi-binary install
OCI images, propulsion / build / push yes yes yes
docker-compose.yml yes, publication as-is yes partial
Overlay networks, Swarm, CRI no yes partial
GPU on the roadmap yes yes

Intel i7-14700KF, Linux 7.0.0, the merchandise binary, 1 book you tin tally yourself: python3 examples/benchmark.py. Yours will disagree pinch your CPU, kernel and filesystem.

kern bubblewrap runc podman docker
Cold commencement (bare box) ~2.3 ms ~2.3 ms ~18.6 ms ~293 ms ~297 ms
200 boxes successful parallel ~0.11 s ~0.16 s ~0.35 s ~44.8 s ~16.2 s

Three 1000 astatine erstwhile return ~2.2 s, and a unrecorded container costs ~0.3 MB of memory.

Two honorable notes. Nobody wins single-shot latency outright: the level for unshare + exec is 1 to 2 ms, truthful the full apical tier sits wrong its ain noise, and bubblewrap is simply a launcher pinch no images, caps aliases lifecycle. The spread that intends thing is to the engines, 2 orders of magnitude above.

Method, per-phase breakdown, committee numbers and each caveat: BENCHMARKS.md.

Namespaces, a pivot_root, 16 vulnerable capabilities dropped earlier exec, an always-on seccomp allowlist by default (moby's ain default select minus kern's 35 flight syscalls, which stay hard-killed; a syscall extracurricular the vetted group returns ENOSYS, and the wider denylist is the opt-out via KERN_SECCOMP=denylist), cgroup v2 limits (--require-limits refuses to commencement unless they bind), and a deny-by-default /dev. Where a bound is cooperative alternatively than kernel-enforced, SECURITY.md says truthful and names the bypass.

You do not person to return it connected trust: pentest/ holds 4 adversarial suites that assert those boundaries against the kernel alternatively than against kern's ain reporting, and they tally without a registry relationship aliases a network.

sh pentest/run-with-local-registry.sh ./target/release/kern pentest/pentest-ports.sh

Report a vulnerability privately via GitHub Security Advisories aliases [email protected].

docs/INSTALL.md install connected Linux, WSL2 and ARM boards, from source
docs/DOCKER-COMPAT.md what of Docker works, what does not, and wherever it differs
docs/RESOURCES.md · docs/CONFIG.md · docs/STORAGE.md · docs/EGRESS.md the two-verb model, the kern.toml schema, volumes and egress
docs/THREAT_MODEL.md · SECURITY.md · OPEN_ITEMS.md the threat exemplary (structured, past per-mechanism), and the known gaps
BENCHMARKS.md · EDGE.md measurements, and moving connected a Pi, Jetson aliases UNO Q
examples/ · blog/ ninety runnable scripts, and longer write-ups
bindings/python/README.md · bindings/node/README.md the kern-sandbox SDK: embed kern successful Python aliases Node

The halfway is done. Everything supra useful today: 840 Rust, 78 Python and 61 Node tests, clippy-clean, cargo-deny-clean, connected existent hardware: Linux, WSL2, Raspberry Pi 5, Jetson Orin Nano, Arduino UNO Q. v0.7.0 is the first published release. The CLI and config aboveground tin still change, ever called retired successful CHANGELOG.md.

Issues and propulsion requests are welcome. CONTRIBUTING.md has the workflow and the gates; contributions are covered by the CLA.

Alex, @realexhub. Commits travel from @getkerndev, the project's perpetrate identity.

The commits are not signed; the merchandise TAG is. That is what to verify: git verify-tag v0.7.0 against the cardinal successful provenance/, whose fingerprint is in SECURITY.md.

Apache-2.0. See LICENSE and TRADEMARK.md.

More