Quick Start
The project officially supports two primary runtimes. Choose the one that matches your background and requirements:
| Feature | Podman | AppJail |
|---|---|---|
| The experience | Familiar — feels like Docker on Linux | Native — built for the FreeBSD way |
| Best for | Quick migrations and compose.yaml fans |
Production stability and deep system control |
| Primary tool | podman / podman-compose |
appjail / appjail-director |
| Config format | Standard compose.yaml |
Director YAML + Makejail |
| Networking | CNI-based (bridge, host) | Native VNET, IP aliases, virtual nets |
| Privileges | Root only (on FreeBSD) | Unprivileged via doas delegation |
Both are first-class citizens in the Daemonless ecosystem, and it's safe to use both on the same host — they manage separate configuration and storage. Any other OCI-compatible tool with FreeBSD support should work too.
Podman#
Root privileges required — Podman on FreeBSD currently requires root (rootless mode isn't supported yet). Run every command in this guide as root or via sudo/doas.
pkg install podman-suite sysutils/podman-composeocijail version 0.5.0+ is required for .NET applications (Radarr/Sonarr) and PostgreSQL — it natively supports the jail parameters those apps need via OCI annotations.
Configure the kernel to allow packet filtering for local traffic and mount fdescfs:
# Load pf (required before setting pf sysctls)
kldload pf
sysrc kld_list+=pf
# Enable pf filtering for jails
sysctl net.pf.filter_local=1
echo 'net.pf.filter_local=1' >> /etc/sysctl.conf
# Mount fdescfs
mount -t fdescfs fdesc /dev/fd
echo 'fdesc /dev/fd fdescfs rw 0 0' >> /etc/fstabAdd to /etc/pf.conf (replace em0 with your external interface):
ext_if=em0
# Podman container networking
rdr-anchor "cni-rdr/*"
nat-anchor "cni-rdr/*"
table <cni-nat>
nat on $ext_if inet from <cni-nat> to any -> ($ext_if)
nat on $ext_if inet from 10.88.0.0/16 to any -> ($ext_if)
Reload it:
pfctl -f /etc/pf.confsysrc podman_enable=YES
service podman startStart with Tautulli — a lightweight Python app that doesn't require special jail permissions:
podman run -d --name tautulli \
-p 8181:8181 \
-e PUID=1000 -e PGID=1000 \
-v /path/to/containers/tautulli:/config \
ghcr.io/daemonless/tautulli:latestpodman ps
podman logs -f tautulliAccess the UI at http://localhost:8181.
.NET applications need one more flag#
Apps like Radarr and Sonarr require the allow.mlock jail annotation:
podman run -d --name radarr \
-p 7878:7878 \
--annotation 'org.freebsd.jail.allow.mlock=true' \
-e PUID=1000 -e PGID=1000 \
-v /path/to/containers/radarr:/config \
ghcr.io/daemonless/radarr:latestSecurity model: host root ≠ container root#
If you're coming from Docker, "requires root on the host" might set off alarm bells — on Linux that can genuinely mean weaker isolation. On FreeBSD it doesn't, because host privilege and workload privilege are two separate axes:
- Host privilege is an admin requirement, not a workload property. Podman/AppJail need root on the host to create and manage jails, the same way
jail(8)itself always has, and the same way Docker's own daemon runs as root on Linux. - Daemonless images drop privileges immediately. The base image's s6 supervisor starts as root only long enough to do required setup (fixing
/configownership, etc.), then execs the application vias6-setuidgid bsd— dropping to an unprivileged user (UID:GID 1000:1000 by default, remappable via PUID/PGID) before the app itself starts.
The container boundary is a real FreeBSD jail — the same kernel-level isolation primitive jail.conf setups have used for two decades, not a namespace approximation of one.
Using ZFS#
If you're on ZFS, configure Podman to use it for copy-on-write layering and snapshot support:
zfs create -o mountpoint=/var/db/containers/storage <pool>/podmanSee ZFS Storage for storage.conf tuning.
AppJail#
AppJail is the native FreeBSD alternative — unprivileged management via doas delegation, VNET networking, and deeper jail integration. It uses the same OCI images as Podman, deployed with appjail oci run or an appjail-director.yml descriptor instead of compose.yaml. See daemonless.io/guides/quick-start for the full AppJail walkthrough, including host configuration and the Director CLI.
Once a container is running, map file ownership correctly and pick a networking mode.