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
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.