====== Create a FreeBSD Jail ======
This page documents the standard way of creating a new FreeBSD jail on **bane**: build (or reuse) a FreeBSD release image, clone it into a new dataset with ZFS, write the jail config, and start the jail with ''jailer''.
All commands are run on **bane** as **root** -- SSH to bane and become root first.
----
===== Building a FreeBSD release image =====
Jails are cloned from a clean FreeBSD **release image** -- a minimal base install kept in its own ZFS dataset under ''zbane/jails/image/''. The image is built once per release (e.g. ''15.1-RELEASE'') and then reused for every new jail.
==== 1. Create the dataset ====
Make a dataset for the release version under ''zbane/jails/image/'':
zfs create zbane/jails/image/15.1-RELEASE
==== 2. Install the base system ====
Go to the dataset's mountpoint:
cd /usr/local/jails/image/15.1-RELEASE
Then install the base system -- the same release that is on your host -- into that folder:
bsdinstall jail .
''bsdinstall'' will ask which distribution sets to install (''base'', ''doc'', ''lib32'', ''src'', ...). **You only need the ''base'' set** -- uncheck everything else. The result is a minimal FreeBSD userland (no ''lib32'', ''doc'', ''src'' or ''ports'').
==== 3. Snapshot the image ====
Take a snapshot of the finished image. By convention the snapshot is named after the patch level the base install reports (''bsdinstall'' installs the base already patched to the current errata level):
chroot /usr/local/jails/image/15.1-RELEASE /bin/freebsd-version -u # -> 15.1-RELEASE-p6
zfs snapshot zbane/jails/image/15.1-RELEASE@p6
New jails are then cloned from this snapshot.
----
===== Creating a jail from the image =====
==== 1. Generate a UUID ====
uuidgen -r
Use the **first segment** of the generated UUID (the first field): for ''f9ba13cb-e512-454e-be10-a7092ed0e3e9'' use ''f9ba13cb''. It ends up in the jail's VNET interface name (''j${uuid}ng0''), so every jail gets a unique interface.
==== 2. Clone the image into the new jail ====
Clone the release image into the new jail's dataset with ''zfs send / recv'':
zfs send zbane/jails/image/15.1-RELEASE@p6 | zfs recv -v zbane/jails/passwd-01
The destination dataset must not exist yet -- ''zfs recv'' creates it. It inherits its mountpoint from ''zbane/jails'' and appears under ''/usr/local/jails/passwd-01'', exactly the ''path'' used in the jail config below.
==== 3. Write the jail config ====
Jail config files live in ''/etc/jail.conf.d'' (the host's ''/etc/jail.conf'' includes every ''*.conf'' in that directory). Create ''/etc/jail.conf.d/name-01.conf'' using this template:
name-01 {
$uuid = "";
devfs_ruleset = 10;
$bridge = "prod";
$domain = "abi.am";
$if_name_0 = "j${uuid}ng0";
vnet;
vnet.interface = "${if_name_0}";
exec.prestart = "service ngbuddy jail ${if_name_0} $bridge";
exec.start += "/sbin/ifconfig lo0 127.0.0.1 up";
exec.start += "/sbin/ifconfig ${if_name_0} ether 58:9c:fc:ab:10:12";
exec.start += "/sbin/ifconfig ${if_name_0} inet 172.20.200.102/24";
exec.start += "/sbin/route add default 172.20.200.1";
exec.start += "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown jail";
exec.poststop += "service ngbuddy unjail ${if_name_0} ${name}";
host.hostname = "${name}.${domain}";
path = "/usr/local/jails/${name}";
exec.consolelog = "/var/log/jail/${name}.log";
persist;
depend = pr-01, log-01, dns-01;
}
Values to fill in:
* **Block name / file name** -- the block name (''name-01'') and the config file name must match the jail's name (''${name}'' expands to it at runtime).
* **''$uuid''** -- the first segment of your generated UUID (e.g. ''f9ba13cb'' -> interface ''jf9ba13cbng0'').
* **''ether''** -- a free MAC address in the ''58:9c:fc:ab:xx:xx'' range; must be unique per jail.
* **''inet''** -- a free IPv4 address in ''172.20.200.0/24'' (the jail network; gateway ''172.20.200.1'').
* **''$domain''** -- ''abi.am'' for externally visible jails, ''local.abi.am'' for internal ones.
* **''depend''** -- the jails that must be running before this one starts.
Everything else stays as-is: the ''exec.prestart''/''exec.poststop'' hooks call ''ngbuddy'' to create and destroy the jail's netgraph interface on the ''prod'' bridge automatically, and ''exec.consolelog'' sends the jail's console output to ''/var/log/jail/name-01.log''.
==== 4. Start the jail ====
jailer start name-01
That's it -- the jail is up. Verify with:
jls
jailer list
and get a shell inside the jail with:
jailer console name-01
----
===== See also =====
* [[infra:backup_dataset|Backup a Dataset]] -- uses the same ZFS send/recv mechanism
* [[infra:servers:UpdatingFreeBSD|Update FreeBSD Servers]]
* [[infra:howto:start|How To's]]
If you created a new jail, add an entry to [[infra:changelog]].