How to use FreeBSD Jails with ZFS

In this guide we will see how to use FreeBSD Jails using the Z file system. This will allow us to create a skeleton jail (i.e., a base image) that will be copied every time we need to deploy a new service. One of the key advantages of this approach is that each Jail is initially identical to the skeleton, meaning that it consumes virtually no additional space until its data begins to diverge from the template.

Create a ZFS partition

To avoid assuming any prior steps, we will begin by creating a new ZFS partition:

# gpart add -t freebsd-zfs da0s1 # replace 'da0s1' with your actual disk

Next, let’s enable pool auto mount at boot by adding the following entry to the /etc/rc.conf file:

 zfs_enable="YES"

Create a new pool

Let’s now proceed by creating a new ZFS pool on the /dev/da0s1b partition:

# zpool create zroot /dev/da0s1b

Then, we can create two datasets called jails and jails/template, respectively. The former will store the actual Jails while the latter will provide a skeleton for creating new services.

# zfs create -o mountpoint=/usr/jails zroot/jails
# zfs create zroot/jails/template

Create the template Jail

Let’s now set up the template Jail. We will begin by installing the base system on it using bsdinstall(8). When prompted, we will omit the root password.

# bsdinstall jail /usr/jails/template 

Now, we can proceed by creating a snapshot of the template Jail

# zfs snapshot zroot/jails/template@clean 

The base template is ready for duplication, let’s try it.

Create a new Jail

We are now ready to create a new Jail using the template we have created in the previous step.

# zfs clone zroot/jails/template@clean zroot/jails/www 

Before starting it, let’s create a simple configuration file under /etc/jail.conf:

# Get Jail path
$jail_path="/usr/jails";
path="$jail_path/$name";

# Global settings
mount.devfs;
exec.clean;
exec.start="sh /etc/rc";
exec.stop="sh /etc/rc.shutdown";
allow.raw_sockets=1;
ip4=inherit;

www {
    host.hostname="www";
}

Then, let’s add the following two lines to /etc/rc.conf:

[...]
jail_list="www"
jail_enable="YES"

Finally, to start the Jail, we can issue the following command:

# service jail start www

We can retrieve a list of running jails by running jls:

# jls
   JID  IP Address      Hostname                      Path
     1                  www                           /usr/jails/www

The Jail is now up and ready to use. As an example, we will try to configure the nginx web server:

# pkg -j www install nginx
# sysrc -f /usr/jails/www/etc/rc.conf nginx_enable="YES"
nginx_enable: NO -> YES
# service -j www nginx start

Testing

Let’s now make sure that the web server is running on port 80. We can verify that by issuing the following command:

# sockstat -46l
USER     COMMAND    PID   FD  PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      
www      nginx       1005 6   tcp4   *:80                  *:*
root     nginx       1004 6   tcp4   *:80                  *:*

Destroying a Jail

Last but not least, to remove a Jail, shut it down using service jail stop www, destroy its ZFS dataset using:

# zfs destroy zroot/jails/www

Then, remove it from the jail_list variable on /etc/rc.conf.