Skip to content
Jesús Daniel Colmenares Oviedo edited this page Dec 21, 2023 · 1 revision

Using ZFS inside a jail

ZFS is one of the most advanced file system with volume management capabilities available today with many useful features. ZFS is one of the file systems offered by FreeBSD and best of all it has support for jails, so we can delegate a dataset inside a jail and use it as we use a dataset on the host.

To delegate a dataset we need to mark one with the jailed property set to on. I recommend doing this task at creation time because it avoids creating a directory indicated by the mountpoint property, so our host will not make a mess.

zfs create -o jailed=on -o mountpoint=/jailed zroot/jailed

If we check /jailed:

# ls /jailed
ls: /jailed: No such file or directory

Perfect! The directory is not created.

In AppJail the configuration file used by each jail is called Template, we need one to attach or detach a dataset and also to mount those datasets inside the jail. (I will explain this a bit better in a moment.)

exec.start: "/bin/sh /etc/rc"
exec.stop: "/bin/sh /etc/rc.shutdown jail"
allow.mount
allow.mount.zfs
enforce_statfs: 1
${dataset}: zroot/jailed
exec.poststart: "zfs jail ${name} ${dataset}"
exec.poststart+: "appjail cmd jexec ${name} zfs list -Hro name ${dataset} | xargs -L 1 appjail cmd jexec ${name} zfs mount"
exec.prestop: "appjail cmd jexec ${name} zfs list -Hro name ${dataset} | tail -r | xargs -L 1 appjail cmd jexec ${name} zfs umount"
exec.prestop+: "zfs unjail ${name} ${dataset}"

We already have all the requirements to delegate a ZFS dataset, so we can proceed to create the jail, but with some important options.

appjail quick jzfs \
    mount_devfs \
    device='include $devfsrules_hide_all' \
    device='include $devfsrules_unhide_basic' \
    device='include $devfsrules_unhide_login' \
    device='path zfs unhide' \
    template=template.conf \
    overwrite=force \
    start

overwrite=force is not necessary if you do not want to remove an existing jail named jzfs. The rest of the options are important because with

  • mount_devfs we mount a devfs(5) filesystem, to be able to manage devices inside the jail;
  • template to indicate the template we will use;
  • start to start the jail;
  • and each device option to specify dynamically the devices needed by the jail. The highlighted device is zfs because it is very important for the zfs(8) command.

Now that we have our jail created, we can log into it and issue some ZFS commands.

# appjail cmd jexec jzfs
root@jzfs:~ # zfs list -r
NAME           USED  AVAIL     REFER  MOUNTPOINT
zroot         79.1G   204G       96K  none
zroot/jailed    96K   204G       96K  /jailed
root@jzfs:~ # zfs mount
zroot/appjail/jails/jzfs/jail  /
zroot/jailed                    /jailed

The attentive reader has noticed that there is a dataset named zroot/appjail/jails/jzfs/jail that does not exist on our jail. This is very important to note because zfs umount -a will fail, this is the reason why we explicitly mount or unmount (all of them recursively) the datasets specified in our template.

Let's create a new dataset and assign it a quota of 1g.

root@jzfs:~ # zfs create -o quota=1g zroot/jailed/users
root@jzfs:~ # zfs list -r
NAME                 USED  AVAIL     REFER  MOUNTPOINT
zroot               79.1G   204G       96K  none
zroot/jailed         192K   204G       96K  /jailed
zroot/jailed/users    96K  1024M       96K  /jailed/users
root@jzfs:~ # pw useradd -n op -m -d /jailed/users/op
root@jzfs:~ # su - op
Sometimes a single slow HDD can cripple the performance of your entire system.  You can spot one like this:

# gstat -I5s | sort -rn -k9 | head

                -- Alan Somers <asomers@FreeBSD.org>
op@jzfs:~ $ dd if=/dev/random of=randomfile status=progress bs=1m
dd: randomfile: Disc quota exceedediB) transferred 62.471s, 17 MB/s

1024+0 records in
1023+1 records out
1073086464 bytes transferred in 62.848765 secs (17074106 bytes/sec)
Clone this wiki locally