Overview

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#

1
Install 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-compose

ocijail 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.

2
Enable networking

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/fstab
3
Configure the firewall

Add 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.conf
4
Start Podman
sysrc podman_enable=YES
service podman start
5
Run your first container

Start 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:latest
podman ps
podman logs -f tautulli

Access 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:latest

Security 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:

  1. 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.
  2. Daemonless images drop privileges immediately. The base image's s6 supervisor starts as root only long enough to do required setup (fixing /config ownership, etc.), then execs the application via s6-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>/podman

See 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.

Next steps

Once a container is running, map file ownership correctly and pick a networking mode.

Updated

Was this page helpful?