SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-29

FreeBSD jails vs Docker containers: wetin differ?

FreeBSD jail dey isolate complete userland, while Docker dey use layered images from registry. See how software, state, networking and limits really differ.

FreeBSD jails vs Docker containers, for one paragraph

FreeBSD jails and Docker containers dey solve the same problem for two different ways. Both dey run isolated userlands for one shared kernel, so none of dem be virtual machine. The difference na wetin dey inside dem. Docker container dey run one process from layered image wey you pull from registry. Jail dey run complete FreeBSD userland: e get im own /etc, im own rc startup scripts, im own pkg database, and any number of processes wey you want. Almost every other difference for this page follow from that one difference.

SSD Nodes no dey offer FreeBSD images. You no fit rent FreeBSD server for this platform, and nothing wey dey below na install guide for machine wey you fit buy here. This na comparison of two isolation models. E dey written so you fit know which one workload really need, and so you fit read how FreeBSD team set up dem system without guessing.

Wetin jail really be

Jails show for FreeBSD 4.0 for March 2000, so dem old pass cgroups and dem be roughly ten years older than Docker. The mechanism na one kernel call. jail(8) takes directory tree and starts processes inside am with jail ID attached, then kernel dey reject one fixed set of operations for any process wey carry that ID. Process wey dey inside jail no fit see processes outside the jail, no fit mount or unmount filesystems, no fit load kernel modules, and no fit bind to network addresses wey the jail no receive. No separate namespace type dey to learn, and no need opt in feature by feature: restrictions come as one unit, adjusted through parameters for jail config.

For host, jls lists jails wey dey run, while jexec web sh puts you inside shell for the jail wey dem name web.

You build jail by putting FreeBSD userland inside directory. The base system go do that for you:

sudo bsdinstall jail /usr/local/jails/containers/web

That command go fetch base distribution set for your release and run normal post-install steps. So you go set root password and choose timezone exactly as you go do for new server. The result na FreeBSD installation wey dey inside folder. Then you describe am for /etc/jail.conf:

web {
  host.hostname = "web.example.internal";
  path = "/usr/local/jails/containers/web";
  ip4.addr = "10.0.0.10";
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  mount.devfs;
}

Start am, then check am:

sudo service jail start web
jls

jls suppose now list web with JID, hostname, and IP address. If jail no show, run sudo jail -c web directly. E go apply the same configuration for foreground and print the parameter wey e no fit accept, instead make the failure remain inside service output.

The line wey worth reading twice na exec.start = "/bin/sh /etc/rc". When you start jail, e dey run FreeBSD normal boot script inside am. So the jail go start every service wey dey enabled inside its own /etc/rc.conf. Docker container no get equivalent step, because e dey run the image entrypoint process and stop when that process stop.

How you dey bring software in: images and registries versus userland wey you fill

Na this difference you go feel from day one.

With Docker, you name software and receive am. docker pull nginx dey fetch layered, content-addressed image wey another person build and test, then docker compose up -d start am with the volumes and network wey attach to am. Registry na the product. Plenty of the value for Docker workflow come from thousands of projects wey publish working image. Na this one make running Docker for VPS quick work instead of full project.

FreeBSD no ship with default public registry for jail images. You create empty userland and install software inside am, the same way you go set up bare server. This one need more typing. But e dey more transparent, because wetin dey run for the jail na wetin pkg put there, from the same package set wey host dey use.

Tooling dey make the process short. BastilleBSD na the common jail manager, and e dey available as package:

sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASE

bastille setup dey configure networking, storage and firewall for you. bastille bootstrap download release once, and every jail wey you create later go reuse am. FreeBSD 15.1 na the current production release, wey come out for June 2026; replace am with the release wey you dey run.

After that, you fit create jail with one command, then fill am with one more:

sudo bastille create web 15.1-RELEASE 10.17.89.10/24
sudo bastille pkg web install nginx
sudo bastille service web nginx start
sudo bastille console web

bastille console web give you login shell inside the jail, while bastille list show wetin dey exist for host. To repeat build, Bastille templates dey keep the steps for file and apply dem to jail. Na this be the closest thing for this world to Dockerfile. System dey replay template for every jail. Nothing dey arrive prebuilt.

So the honest summary short. Docker hand you builds wey other people make. Jails hand you installs wey you make yourself. If software for your shortlist ship as container image and nothing else, that one don settle the matter before any other factor fit get vote.

State and upgrade: the part wey ZFS dey change

Docker dey split state on purpose. The container filesystem fit throw away, your data dey live for named volume or bind mount, and upgrade na docker compose pull followed by docker compose up -d. Dem go replace the container, and anything wey you no put for volume go disappear. This na feature when you follow the rule, but e go become data-loss incident when you forget am. Na why the choice between bind mounts and named volumes get plenty weight for Compose stack.

Jail no dey split state, and na ZFS make this arrangement work. The whole jail na one dataset:

sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgrade

Use zfs list check the real dataset name before you run this. The path above na the layout wey the handbook use. The snapshot dey take about one second, and e dey use almost no space until the jail contents change. If upgrade spoil the service, rollback go return the whole userland to the earlier state. This one include the package database and the config files wey you edit by hand at 2am. Docker no get built-in equivalent because its model assume say you no need one.

zfs clone na the other half. Clone of snapshot na new writable jail wey share unchanged blocks with its parent. So, staging copy of 3 GB jail go use almost no disk space until you start changing am. Na so FreeBSD admin dey build jail wey be "same as production" to rehearse upgrade.

Base system upgrade dey separate from packages. For jail wey hold its own copy of the userland:

sudo freebsd-update -b /usr/local/jails/containers/web fetch install

Thin jails dey avoid repeating that work. Dem mount one shared read-only base through nullfs and give each jail small writable layer of its own. This mean say you patch the base once, and every jail go see the result. Bastille dey create thin jails by default.

Networking: published ports versus addressing decision

Docker dey decide networking for you and e ask you to publish only the exceptions. Containers dey land for a bridge, dem fit reach each other with service name for user-defined network, and -p 8080:80 exposes one of dem to the host. Docker dey write im own packet filter rules to make this work. Na this same process make published container port fit pass straight through ufw.

Jail make you choose the model from the beginning, and two models dey.

Shared IP. ip4.addr = "10.0.0.10" add that address to an existing host interface and restrict the jail to am. The jail no get im own network stack, so e no fit run im own firewall. E also no fit really bind to every address: when jailed socket ask for 0.0.0.0, kernel go rewrite am to the jail own address. Two jails no fit both listen on port 80 for the same address, so you give each one separate address, or put reverse proxy in front.

VNET. Add vnet; to the jail and e go get complete network stack: im own interfaces, im own routing table, and im own firewall rules. You connect am to the host with epair, wey be virtual cable with one end for each side, then put the host end on a bridge. This one resemble Docker networking pass, and na the mode behind Bastille -V and -B jail types.

To forward host port into a jail, use pf redirect rule. Bastille wrap am:

sudo bastille rdr web tcp 80 80

No EXPOSE dey here, and no automatic publishing dey happen. Nothing fit reach a jail unless im address or redirect rule allow am. This one dey slower to set up, but e make firewall quieter.

Resource limits: cgroups vs rctl

Docker dey limit container with cgroups, and the limits dey where dem define the container: --memory=1g --cpus=1.5 for command line, or the matching keys for Compose file. If you already dey keep your stack for Docker Compose file for VPS, the limit dey beside the service wey e apply to and e dey follow am for git.

FreeBSD dey use rctl, and na subsystem wey you must switch on. Resource accounting dey off by default because e dey cost small for every allocation. Add the tunable to /boot/loader.conf and reboot:

kern.racct.enable=1

Then set one rule and monitor am:

sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:web

rctl -hu jail:web dey print the jail current usage with human-readable units, so you fit see how close e dey to the limit before anything spoil. The deny action dey make over-limit allocation fail inside the jail, so you go see the application own allocation error instead of kill message for the host.

Rules wey you add with rctl -a go disappear for the next reboot. FreeBSD rctl service dey reload dem from /etc/rctl.conf, so write the rule inside that file and enable the service:

sudo sysrc rctl_enable=YES

Na for this area Docker dey clearly easier. Limit wey dey for Compose file dey reviewed together with the service wey e constrain. An rctl rule na line for separate file wey name a jail defined for another place.

When answer na virtual machine: bhyve

A jail dey share host kernel, so some things no dey possible at all. E no fit run different kernel version, e no fit load kernel module, and e no fit run Linux binaries the way Linux container dey do. FreeBSD get Linux compatibility layer, wey dem dey call linuxulator, but e only implement part of Linux system calls. E no be general solution for any kind Linux images.

bhyve na FreeBSD hypervisor. Na the correct tool when you need real boundary between machines: different operating system, different kernel, or tenant wey you no wan make e share kernel. You go use more memory because you reserve am instead of sharing am, and you go need patch another kernel. Na the same decision wey you make for Linux between containers and full virtual machines. This decision determine whether you need VPS wey support nested virtualization underneath.

The ecosystem, wey be the real reason most teams dey use Docker

Everything wey dey above na about the model. Wetin dey decide the choice for most teams na how big the ecosystem around each one be.

Docker bring Docker Hub and GHCR, docker compose, Kubernetes when one box no longer enough, CI runners wey already get container support configured, and one-command quickstart for almost every project's README. Jails bring the FreeBSD ports tree, wey big and dem dey maintain am carefully, plus smaller number of ready-to-run application bundles. When project publish container image and nothing else, the FreeBSD way na to read the documentation and assemble the parts by yourself.

Jails get their place for the other side of that trade-off. You go want use dem when you already dey run ZFS and you value snapshot and rollback for complete service, when your services na FreeBSD native, when you want complete userland for each tenant instead of one process, or when you want make kernel, packet filter, filesystem, and documentation dey maintained together as one system. Na this last point people dey mean when dem call FreeBSD coherent, and the wider comparison of Linux and FreeBSD as server platforms and wetin FreeBSD 15 change for server use explain am more.

One final judgement. If your team already sabi Docker, the cost of moving real, and the benefit suppose get specific reason. No switch because of isolation quality; the two models dey close enough, so your configuration matter more. Switch because you want ZFS-backed rollback for complete services, or because you already dey use FreeBSD.

FAQ

I fit run Docker images for FreeBSD?

No be Linux images, and e no be supported way. FreeBSD get OCI container support: sudo pkg install -y podman-suite installs Podman, wey dey run containers through ocijail, a runtime wey dey create real jails underneath. E need fdescfs mounted on /dev/fd for the container monitor, and pf for container NAT (network address translation). OCI images wey native to FreeBSD dey work best. Linux images still need the Linux compatibility layer, and as of August 2026, dem still describe the FreeBSD Podman port as experimental. If your deployment na stack of Linux images, run am for Linux. For the RHEL family, that means installing Docker on Rocky Linux or AlmaLinux, where Podman show up again as the package wey already own the docker command before you install anything.

FreeBSD jails more secure than Docker containers?

Both of dem share one host kernel, so kernel bug fit affect both, and neither one na boundary wey you go choose for genuinely untrusted code. The difference dey for how dem start. Jail starts with broad set of operations refused, and you re-enable dem one parameter at a time. Docker container starts as root inside set of namespaces, with some capabilities dropped, and extra hardening na opt-in. For real use, configuration decide more than the model: jail wey dey run with allow.mount and allow.raw_sockets enabled no safer pass container wey dem configure carefully.

How I fit back up a jail?

Take snapshot of the dataset and send am. sudo zfs snapshot zroot/jails/containers/web@backup, then zfs send that snapshot go another pool or put am inside file wey you copy comot from the box. Because jail dey keep the whole userland inside one dataset, the snapshot capture the installed packages and data for one consistent point, together with every config file wey you edit by hand. This na opposite of Docker method, where you back up the named volumes and the Compose file, then rebuild everything else from the image.

I need BastilleBSD, or the base system enough?

The base system enough, and na better place to start. jail.conf, jls, jexec and service jail start cover the whole model, and once you sabi dem, you fit read any FreeBSD host without first learning the tooling for that host. Bastille na convenience layer above am: e bootstrap releases, create thin jails, apply templates, and write pf redirect rules for you. Learn the base commands first, then add Bastille when the number of jails make the typing too much.