Vivian Voss

The Replacement: FreeBSD Jails

freebsd docker unix

The Replacement ■ Episode 03

Docker requires a daemon, a container runtime, image layers, a build cache, a registry, volume mounts, and overlay networks. The question that precedes all of this machinery is deceptively simple: what are you actually trying to do?

Process isolation.

FreeBSD Jails have provided process isolation since FreeBSD 4.0 in March 2000. Twenty-six years of kernel-level isolation, no daemon, no images, no layers, no registry. A directory and a configuration file. The technology predates Docker by fourteen years. It predates Docker’s creators’ first commit by a comfortable margin.

The Setup

The entire lifecycle of a jail, from creation to service management to destruction, fits on one screen. No YAML. No Dockerfile. No multi-stage build. No registry login.

# Create and enter
jail -c name=web path=/jails/web \
  ip4.addr=10.0.0.2 command=/bin/sh

# Install software (from the host)
pkg -j web install nginx

# Copy configuration
cp /templates/nginx.conf \
  /jails/web/usr/local/etc/nginx/

# Start the service inside the jail
jexec web service nginx start

# Start and stop the jail itself
service jail start web
service jail stop web

That is the complete workflow. The jail is a directory tree with its own process space, its own network address, and its own package management. You can edit its files from the host. You can make it read-only. You can snapshot it with ZFS before every change and roll back in seconds if the change was wrong.

# Snapshot before a risky change
zfs snapshot zroot/jails/web@before-update

# Something broke? Rollback in seconds
zfs rollback zroot/jails/web@before-update

# Replicate to another server
zfs send zroot/jails/web@backup \
  | ssh mirror zfs recv tank/jails/web
Process Isolation: Two Approaches Docker Since 2013 Docker daemon Container runtime Image layers Build cache Image registry Dockerfile Volume mounts Overlay networks iptables rules Daemon crashes → all containers down FreeBSD Jails Since 2000 jail.conf A directory No daemon No images No layers No registry No overlay network Native kernel isolation ZFS snapshots in ms VNET for real isolation Both achieve process isolation. The difference is the machinery. The best container runtime is the one your kernel already has.

The Advantages

No daemon. Docker runs a persistent daemon process. If the daemon crashes, every container goes with it. Jails are kernel-level constructs. There is no intermediary process to fail. The kernel manages isolation directly. The jail starts when you tell it to and stops when you tell it to. It does not depend on a userspace daemon remaining healthy.

ZFS integration. A jail is a directory tree. A ZFS dataset is a directory tree. The marriage is natural and powerful. Snapshot a jail before an update. Clone a jail for testing. Send a jail to another server with zfs send. Roll back a failed deployment in seconds, not minutes. The filesystem and the isolation layer speak the same language because they were designed by the same community.

Shared base system. Fifty jails can share a single copy of /usr via nullfs mounts. Each jail sees its own root filesystem, but the underlying binaries are shared. Docker achieves something similar with layers, but layers bring complexity: cache invalidation, storage drivers, overlay merges. Nullfs mounts are a filesystem feature. The kernel handles them. There is nothing to misconfigure.

VNET for genuine network isolation. Each jail can have its own virtual network stack: its own interfaces, its own routing table, its own firewall rules. This is not an overlay network bolted on top of iptables. It is a kernel-native virtual network stack. You can inspect it with the same tools you use for the host network. No docker network inspect. Just ifconfig.

The Honest Trade-offs

Docker has its place. For local development with heterogeneous teams, where the alternative is “works on my machine,” Docker provides a common denominator. For CI/CD pipelines that need disposable, reproducible environments, Docker images are convenient. For organisations that have already invested in Kubernetes, the ecosystem integration is real.

What you lose with Jails: Docker Hub’s eight million images, and their eight million potential vulnerabilities. The warm feeling of running someone else’s mystery binaries in production. “Works on my machine” portability across Linux distributions, traded for “works on any FreeBSD” consistency.

What you gain: native kernel isolation without a hypervisor. ZFS integration that makes snapshots, clones, and replication trivial. A shared base system. A network stack you actually understand. Resource limits via rctl. Everything in the kernel. Everything documented. Everything stable for twenty-six years.

The Evidence

Netflix streams 15 per cent of global internet traffic on FreeBSD. WhatsApp scaled to two million connections per server on FreeBSD. PlayStation 4 and PlayStation 5 run a FreeBSD derivative. Juniper builds its router operating system on FreeBSD. NetApp runs storage on FreeBSD.

The technology powers critical infrastructure worldwide. It is not fashionable. FreeBSD has no marketing department. It has no venture capital. It has engineers building open source, quietly, correctly, for three decades. Docker has VC millions and a marketing team. Draw your own conclusions about which factor determines what you hear about.

The Reduction

The best container runtime is the one your kernel already has. Jails do not replace Docker in every context. They replace the assumption that process isolation requires a daemon, an image format, a registry, and an overlay network. It does not. It requires a kernel that understands isolation. FreeBSD’s kernel has understood isolation since 2000.

A directory and a config file. No daemon. No images. No layers. No registry. Native kernel isolation, ZFS snapshots in milliseconds, and a network stack you can actually read. Twenty-six years of stability. The replacement, not the alternative.