jail(2)
Create a FreeBSD jail and lock the current process into it (jail_set/jail_attach for more).
- Since
- FreeBSD 4.0check your kernel →
- System
- FreeBSD
- Man page
- jail(2)
Synopsis
#include <sys/param.h>
#include <sys/jail.h>
int jail(struct jail *jail);
int jail_attach(int jid);
int jail_remove(int jid);
#include <sys/uio.h>
int jail_get(struct iovec *iov, u_int niov, int flags);
int jail_set(struct iovec *iov, u_int niov, int flags);Rod's take
I spent six years saying jails already solved containers and Docker was chroot with a marketing budget. Half right. OCI solved image distribution, and jails never tried. But for one service on hardware you own, a jail on its own ZFS dataset is tighter isolation with less machinery: clone a base snapshot for a copy-on-write root, keep enforce_statfs at 2 so the host's mounts stay invisible, snapshot before every deploy, and roll back in the time it takes ZFS to flip a pointer. If you're shipping to a fleet of strangers, use the container stack. Otherwise, sit with this.
Flags and arguments that matter
| Name | What it does |
|---|---|
| JAIL_CREATE | jail_set(): create a new jail; jid/name must not already exist. |
| JAIL_UPDATE | jail_set(): modify an existing jail. With JAIL_CREATE: create if missing, else modify. |
| JAIL_ATTACH | jail_set(): also attach the calling process, as jail_attach() would. |
| JAIL_GET_DESC / JAIL_OWN_DESC | Return a jail descriptor fd; an owning descriptor removes the jail when closed. |
| JAIL_DYING | jail_get(): allow fetching a jail that is being removed (no effect in jail_set()). |
| enforce_statfs | Jail parameter limiting what statfs(2) and friends reveal: 0 all mounts, 1 only below the jail root, 2 (default) only the root's own mount. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EPERM | Not the super-user, children.max exceeded, or a parameter loosened beyond the current environment. |
| EEXIST | jail_set(): jid or name exists and JAIL_UPDATE is not set. |
| ENOENT | jail_set(): jid or name doesn't exist and JAIL_CREATE is not set. |
| EINVAL | Wrong API version, bad parameter size/range/name, or neither JAIL_CREATE nor JAIL_UPDATE. |
| EAGAIN | No free jail ID could be found. |
Example
# copy-on-write jail root from a clean base snapshot
zfs snapshot zroot/jails/base@clean
zfs clone zroot/jails/base@clean zroot/jails/myapp
# create the jail defined as 'myapp' in /etc/jail.conf
jail -c myapp
# before every deploy
zfs snapshot zroot/jails/myapp@pre-deploy-2026-07-23
# deploy went sideways: stop, roll back, start
jail -r myapp
zfs rollback zroot/jails/myapp@pre-deploy-2026-07-23
jail -c myappTrimmed from the full walkthrough in ZFS boot environments + jails will do 90% of what you're using Docker for, and the other 10% is the part you don't actually need.
Linux equivalent
Linux: no single equivalent; namespaces via clone(2)/unshare(2)/setns(2) plus chroot/pivot_root, assembled by container runtimes.
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
