kern logo kern logo

kern is a real container,
in ~3.5 ms.

kern starts a real container from an OCI image in a few milliseconds: one static binary, rootless, with nothing running in the background. That is cheap enough to give code you have not read yet a container of its own every time it runs, whether that is an agent's tool-call, a CI step or a notebook cell. Compose stacks, CPU and memory slices, Python and Node SDKs and an MCP server come with it.

Compatibility. A box is made of Linux kernel features, so kern needs a Linux kernel under it. Runs on Linux and on ARM boards directly, and on Windows and macOS through a Linux VM: WSL2 on Windows, colima or Lima or OrbStack on a Mac. In the VM it is the same binary and the same CLI, not a port.

Debian and Ubuntu, Fedora, the RHEL family, openSUSE, WSL2, and ARM boards: each release runs the full suite on those, in real VMs and on the boards, before it ships.

Linux · ARM boards (Pi, Jetson, Arduino UNO Q)
$ curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh
Windows · sets up WSL2 for you
PS> irm https://raw.githubusercontent.com/getkern/kern/main/install.ps1 | iex
macOS · two steps: a Linux VM, then kern inside it
$ brew install colima && colima start && colima ssh
VM $ curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh

The second line runs inside the VM, where you are on Linux: it is the same installer as the first block, and you get the ordinary Linux kern, the same binary and the same CLI. There is no native macOS port and there will not be one, because macOS has no namespaces and no cgroups, so there is nothing there for kern to build a box out of; the installer says so and stops rather than dropping a Linux binary on a Mac. Lima, OrbStack, UTM or the VM Docker Desktop already runs work the same way, and no GPU or GPIO is reachable from a Linux guest on a Mac.

Every route verifies the SHA256 before installing, and both scripts are readable first: install.sh, install.ps1.

~3.5 msto a box from an OCI image
one filestatic binary, no daemon
0processes running between boxes
rootlessalways, not opt-in

One machine, one workload, rounded up from the measurement: an Intel i7-14700KF on Linux 7.0.0, with the release binary and python3 examples/benchmark.py. The table below has the numbers without an image, and yours will differ with your CPU, kernel and filesystem.

What kern does

A throwaway shell in a real OCI image

Real images: pull, build from a Dockerfile, commit, push, save and load.

kern box dev --image alpine -it -- sh

The sandbox an AI agent can afford to use on every call

An agent's tool-call, a model's generated snippet, a notebook cell: code that runs before anyone reads it gets its own box, thrown away after. Network off, memory and PID caps the kernel enforces, capabilities dropped, and a deadline applied from outside the box. A timeout, an OOM-kill or a blocked syscall comes back as a typed field on the result, not as an exception to catch: an agent loop reads a field and decides. kern-sandbox on PyPI and npm, a LangChain code tool, an extension for the pi coding agent, and an MCP server that hands Claude Desktop or Cursor a real box to execute in. Those are how your program calls kern; the code inside the box can be in any language, because the box is an OCI image: Python, Node, Go and Rust all run with the same command and a different image.

$ pip install kern-sandbox     # or: npm install kern-sandbox

from kern_sandbox import run_code
r = run_code("print(sum(range(100)))")
print(r.stdout, r.fault)   # a timeout or OOM is data, not an exception

Untrusted code, one flag for the whole bundle

User, PID, mount, network, UTS and IPC namespaces, a pivoted root, a deny-by-default seccomp allowlist, capabilities dropped and cgroup v2 limits.

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

Resource slices, with or without a sandbox

CPU, memory, disk and devices declared once in a kern.toml and attached by name. kern run applies the same caps to a plain host process.

kern box train --image alpine vcpu:heavy vdisk:scratch
kern run --memory 256M --cpus 0.5 -- ./crunch

The docker-compose.yml you already have

kern speaks Docker's formats, with no conversion step and no Docker Desktop.

kern compose compose.yaml up

How a box is built

No daemon means there is nothing to ask and nothing to wait for. The work happens in the process you started, in a fixed order, and kern box --plan prints that order without running any of it.

  1. Namespaces first. User, PID, mount, network, UTS and IPC are unshared up front. Your uid maps to root inside the box and only there, so no privilege is gained on the host.
  2. A root to pivot into. The image is assembled as an overlay, or mounted read-only, then entered with pivot_root. That sequence is a typestate in the source: code that pivots into a root it has not yet remounted read-only does not compile, so the bug is unrepresentable rather than untested.
  3. Capabilities dropped. Sixteen dangerous capabilities go before exec. --security-profile untrusted drops the lot and adds a read-only root.
  4. Seccomp, on by default. The filter is an allowlist, not a blocklist: the default moby set minus the 35 syscalls kern treats as escape vectors, which stay hard-killed. A syscall outside the vetted set gets ENOSYS back instead of a kill, which is what lets ordinary images keep working under it.
  5. cgroup v2 limits. Memory, CPU and pids are written to the box's own cgroup, and --require-limits refuses to start at all if they would not bind, rather than reporting a cap that quietly does nothing.

Then exec. What is left running is one short-lived process, and kern ps reads its state from the kernel rather than from a database that could disagree with it.

Where it fits

kern is built for code you chose to run and whose blast radius you own.

  • Agent tool-calls. A fresh box per call from Python or Node, network off and caps on, or through the bundled MCP server so Claude Desktop and Cursor get a local code interpreter with no cloud account.
  • CI jobs and build steps. Nothing to install on the runner and nothing left running after: one static binary, no daemon to start first.
  • Notebook-style cells. A warm interpreter across calls when you want speed, a throwaway box per call when you want isolation. Timeouts and OOM kills come back as data on the result.
  • Edge and ARM boards. One binary you copy onto a Raspberry Pi, a Jetson or an Arduino UNO Q and run, where a container engine is too heavy to install.
  • The local dev stack. Your existing compose file, brought up with no Docker Desktop and no background VM.

Cold start: kern vs Docker, Podman, runc and bubblewrap

bare box200 in parallel
kern ~2.4 ms ~0.11 s
bubblewrap ~2.6 ms ~0.13 s
runc ~13.1 ms ~0.29 s
docker ~288 ms ~16.7 s
podman ~297 ms ~43.1 s

kern is faster than bubblewrap, by about 9%, and it takes care to show it: timed one runtime after the other both read 2.5 ms, because the gap is smaller than the drift between two batches minutes apart. With alternating batches on an idle machine, against the binary attached to the release rather than a local build, they separate: 2.4 ms against 2.6, kern ahead in 238 of 240 batches. Over 35 replicas the direction has never flipped. bubblewrap is a launcher with no images, caps or lifecycle, and the gap that means something is the one to the engines, two orders of magnitude above. Method, board numbers and every caveat: BENCHMARKS.md.

What kern is not

  • Not a hypervisor. The boundary is the Linux kernel, so a kernel privilege-escalation bug is an escape. Docker and Podman share that condition, which is why gVisor and Firecracker exist.
  • Not free of the userns trade. Its isolation is built on an unprivileged user namespace, a fertile source of kernel LPE bugs.
  • Not a wall around what you mount in. A bind mount is a trust decision you make, not a boundary kern enforces. --net host and --privileged are opt-outs by name.
  • Not shipping GPU slices. No GPU limit ships, so there is nothing here to attack. What ships is the verdict: kern doctor reads sysfs and reports what a VRAM cap on each device would be worth, and on consumer hardware that is a cooperative quota, NOT a boundary against malicious code. Nothing intercepts a driver call and nothing caps a GPU.
  • Not a Docker Engine reimplementation, and not a Kubernetes runtime. It speaks Docker's formats, not its API: no overlay networks, no plugins, no Swarm, no CRI.

Where a boundary is cooperative rather than kernel-enforced, SECURITY.md says so and names the bypass. Five adversarial suites in pentest/ assert those boundaries against the kernel rather than against kern's own reporting.

When to use what

  • Your own or semi-trusted code, fast, on your machine. That is kern: milliseconds per box, nothing running between boxes, the same binary on a laptop and on a board.
  • Production orchestration. Overlay networks, plugins, Swarm or Kubernetes: use Docker, containerd or CRI-O. kern speaks Docker's formats but not its API, and it has no CRI.
  • Hostile code from strangers, on a kernel you serve other tenants from. Use a microVM such as Firecracker, or gVisor. kern's boundary is the Linux kernel, and that is the honest end of what it gives you.

Questions

Does kern run on macOS?

Not natively, and that is a non-goal rather than work left undone: macOS has no namespaces and no cgroups, so there is nothing there for kern to build a box out of. It does run on a Mac inside a Linux VM (colima, Lima, OrbStack, UTM, or the VM Docker Desktop already runs), and there it is the ordinary Linux kern, the same binary and the same CLI. No GPU and no GPIO are reachable from a Linux guest on a Mac.

How is kern different from Docker?

kern has no daemon and no socket: one short-lived process per box, 0 RAM at rest, and a one static binary instead of an always-on engine. It speaks Docker's formats, OCI images, Dockerfiles and docker-compose.yml, but not Docker's API, so there are no overlay networks, no plugins and no Swarm. It is also rootless always, where Docker's rootless mode is opt-in.

Is kern safe for hostile, multi-tenant code?

No. The boundary is the Linux kernel, so a kernel privilege-escalation bug is an escape, and kern's isolation is built on an unprivileged user namespace, a fertile source of those bugs. kern is for code you chose to run and whose blast radius you own: agent tool-calls, CI jobs, build steps, code cells. For hostile code from strangers on a kernel you serve other tenants from, use a microVM or gVisor.

Does kern need root?

No. It is rootless always, not opt-in: your uid maps to root inside the box, and only there. No host privilege is gained.

Can it run my existing docker-compose.yml?

Yes. kern compose compose.yaml up takes the file unchanged, with no conversion step, and runs the stack with no daemon and no Docker Desktop, the same on Linux, WSL2 and ARM boards. One constraint comes with that: a stack is one pod sharing one network namespace, so two services cannot both listen on the same container port, even when their published ports differ. That is what makes a stack start in milliseconds, and the collision is refused by name before anything starts. This is the local dev loop, not a production orchestrator.

How fast is it, really?

A bare box starts in about 2.4 ms and a box from an OCI image in about 3.4 ms, against about 288 ms for docker run, measured on an Intel i7-14700KF running Linux 7.0.0. It is about 9% faster than bubblewrap, measured with alternating batches against the released binary, which is the only way to resolve a gap that small. The gap that means something is the one to the engines.