Certain directories within a jail can be swapped out to (datasets) "outside". This allows this data to be stored independently of the jail. The goal, similar to Docker, is to depend as little as possible on the jail itself. This way, if the jail is corrupted or deleted for some reason, we are able to restore the previous configuration and data with minimal effort. Also, these directories can then be efficiently and regularly backed up via snapshot, which also simplifies further backups. Not to mention moves or migrations.
Last update:
/usr/local/bastille/data/JAILNAME
, which points to the ZFS pool data
in the dataset bastille_data/JAILNAME
/usr/local/bastille/jails/JAILNAME/root/var/db
123
Jails are usually stored in /usr/local/bastille/
with the following directory structure:
└── jails # Jail main directory
└── JAILNAME # Jail Name
└── root # / of the Jail
For this purpose, a new dataset named jails_data
is now created, which hosts the "jail data" and mirrors the above structure somewhat, so that it remains easily traceable. For example, this could be on a completely different pool /mnt/ZPOOLNAME/jails_data
, which may have much more storage space. This is very useful for large data files like pictures or file shares, which have no place in the jail. Also read only if needed.
└── jails_data # As a counterpart to jails.
└── JAILNAME # The same name as in jails, ex. `gitea`
└── DATASET # source directories of the outsourced data, ex. `data`
But how does that come together? Quite simply via NullFS. NullFS virtually puts the contents of directory A on directory B, even across jail boundaries. Very handy! A key factor is access rights and proper permissions. If there is a user USER
with ID 123
in the jail, then the directory outside the jail MUST have the same permissions. Fortunately, the user ID is sufficient for this, so it is not necessary to create the corresponding username in the TrueNAS/FreeBSD host each time as well. Since the directories usually do not yet exist in the jail, they have to be created beforehand. Actually they are created only during the installation of the packages, but this is too late. Because during the installation the content is often already created within the destination directory.
Everything that is executed hereafter happens on the TrueNAS/FreeBSD host. NOT inside the jail!
TrueNAS / Accounts / Groups / Add
GID: ID # ex. 211
Name: NAME # ex. gitea
TrueNAS / Accounts / Users / Add
Full Name: NAME # ex. Gitea User
Username: USERNAME # ex. gitea
Password: PASSWORD # ex. 123
User ID: ID # ex. 211
Primary Group: ID # ex. 211
Shell: nologin # No login allowed
Home: /nonexistent # No home directory
TrueNAS / Storage / Pools / tank/jails_data/JAILNAME / Add Dataset
Name: POOLNAME # ex. data
TrueNAS / Jails / Mount Points / tank/jails_data/JAILNAME / Edit Permissions
User: ID # ex. 211
Group: ID # ex. 211
Practical: The target directories in the jail are created automatically when the mount points are assigned. Also recursive!
TrueNAS / Jails / JAILNAME / Mount Points / Actions / Add
data:
└──Source: SOURCE # ex. /mnt/tank/jails_data/gitea/data
└──Destination: DESTINATION # ex. /mnt/tank/iocage/jails/gitea/root/var/db/gitea
So in the jail GITEA the directory /usr/local/etc/gitea
points to the actually external directory /mnt/tank/jails_data/gitea/data
.
Everything clear? Good!
The jail must be restarted with iocage restart JAILNAME
each time to include these paths.
With iocage fstab -e JAILNAME
the once set entries can be adjusted or removed afterwards.
zfs create -p ZPOOLNAME/jails_data/JAILNAME/DATASET # create SOURCEPATH (dataset) in jail_data
chown -R ID:ID SOURCEPATH # Set user, the naked ID is sufficient, e.g. `211`.
iocage start JAILNAME # start jail
iocage exec JAILNAME mkdir -p TARGETPATH # create directory in jail
iocage fstab -a JAILNAME SOURCEPATH TARGETPATH nullfs rw 0 0 # include directories
iocage restart JAILNAME # restart jail
Voilá
If you find this content valuable and useful, then I'm happy about a feedback via Matrix, follow me on Mastodon or leave a comment here.