Introduction

All articles here are based on the creation of a jail, as I always run each application in its own environment. This ensures clean requirements and no unnecessary mutual dependencies. Jails can run services very simply, leanly and securely. For FreeBSD, it doesn't matter whether one or 100 jails are running at the same time, only the services running in them are relevant. This also makes the whole thing very efficient.

Goals

The goal ist the creation of a jail with the most important basic settings so that other articles can build on it. Important: These instructions have been explicitly designed to run in coexistence with BHYVE on a FreeBSD Server. This means that the storage of the data and the network interfaces are configured in such a way that both are based on the same logic.

NEW: For the very impatient I have a console only section. There are only commands, no explanations.

Last update:

  • 17.01.2026: Small corrections
  • 23.12.2025: Update FreeBSD 15, Small correction. Bastille with fixed MAC Address.
  • 08.12.2024: Update FreeBSD 14.2, final version for public use, updates added
  • 01.12.2024: More tuning
  • 01.12.2024: Outsourcing the network settings to a separate article
  • 15.11.2024: Fine-tuning, integration into the new article: FreeBSD-Server
  • 02.03.2024: Initial (internal) version

Basic requirements

  • A FreeBSD server is set up and prepared
  • The network settings have been made
  • The jails are stored in the following ZFS pool: work in the dataset work/bastille which is then mounted under /usr/local/bastille/
  • OPTIONAL: The data of the jails are stored in the ZFS pool data in the dataset data/bastille,
    which is then mounted under /usr/local/bastille/data
  • Network card of the host system with access to the LAN network: igb0

Bastille

Install

Bastille is quickly installed after adapting the package source with pkg install -y bastille.

Configuration

With service bastille enable Bastille starts automatically on boot.

The following entries are adjusted in the configuration file /usr/local/etc/bastille/bastille.conf:

sysrc -f /usr/local/etc/bastille/bastille.conf bastille_tzdata="Europe/Berlin" # Set time zone
sysrc -f /usr/local/etc/bastille/bastille.conf bastille_zfs_enable="YES"       # Activate ZFS functions
sysrc -f /usr/local/etc/bastille/bastille.conf bastille_zfs_zpool="work"       # ZFS Pool 
sysrc -f /usr/local/etc/bastille/bastille.conf bastille_network_loopback="localnet0" # Network interface for private jails

First, download and unpack the latest FreeBSD Version with bastille bootstrap 14.2-RELEASE update

Optional: If the additional data pool is to be used, it can be created with zfs create -o mountpoint=/usr/local/bastille/data data/bastille. The directory structure per zfs list | grep bastille then looks like this:

NAME                                  USED  AVAIL  REFER  MOUNTPOINT
data/bastille                         96K   1.75T   136K  /usr/local/bastille/data
work/bastille                         651M   430G   136K  /usr/local/bastille
work/bastille/backups                  96K   430G    96K  /usr/local/bastille/backups
work/bastille/cache                   199M   430G    96K  /usr/local/bastille/cache
work/bastille/cache/XX.Y-RELEASE      199M   430G   199M  /usr/local/bastille/cache/XX.Y-RELEASE
work/bastille/jails                    96K   430G    96K  /usr/local/bastille/jails
work/bastille/releases                452M   430G    96K  /usr/local/bastille/releases
work/bastille/releases/XX.Y-RELEASE   451M   430G   451M  /usr/local/bastille/releases/XX.Y-RELEASE
work/bastille/templates                96K   430G    96K  /usr/local/bastille/templates

Before continuing, now is a good time to restart the system with reboot to see if everything is still working.

  • The interfaces
    • publicnet0 with member igb0 and
    • localnet0 with IP Address 10.0.0.1
  • Routing is activated, because sysctl net.inet.ip.forwarding has got the value 1
  • work/bastille mounted to /usr/local/bastille
  • OPTIONAL: data/bastille mounted to /usr/local/bastille/data

The preparations are now complete and the system is ready.

Creating

Jails can now be created in two ways:

  • bastille create JAILNAME XX.Y-RELEASE 10.0.0.2 localnet0 creates a jail with the private network. The jail is initially not accessible from the local network, the required ports are later released via NAT in the pf firewall (as with Docker). The IP address has been assigned manually!
  • bastille create -B -M JAILNAME XX.Y-RELEASE 0.0.0.0 publicnet0 creates a jail which obtains an IP address from the local network via DHCP and is also fully accessible via it (if the pf Firewall is allowing it). The -M option generates a random MAC address, which is then permanently assigned to the jail.

It is best to store the IP address statically in the DHCP server so that it no longer changes and publish it via DNS (JAILNAME.domain.local).

Customization

Jails are relatively strict by default in terms of permissions, i.e. what can be called within the jail and used for system functions. For example,

  • to allow a ping from a localnet0 jail, the allow.raw_sockets function must be enabled, or
  • access to shared memory must be allowed via allow.sysvipc, typically for Postgres. However, this can be set individually for each jail with bastille config JAILNAME set OPTION, in these two examples with:
  • bastille config JAILNAME set allow.raw_sockets 1
  • bastille config JAILNAME set allow.sysvipc 1 It is important to restart the jail afterwards for the option to take effect.

For example, to allow ping for all jails, this can also be set for all jails with bastille config ALL allow.raw_sockets 1.

Access

The jail can now be accessed with the command bastille console JAILNAME. Depending on what is running in it, SSH can also be activated after creating an administrative user. In this case, however, remember to set the root password with passwd.

Updates

Updates are essential for the secure and reliable operation of systems. In addition to the regular updating of packages, this also applies to the base system itself. The following tasks must therefore be carried out regularly:

Console

Voilá