* use the `+jail ..+` command to run a container, given a filesystem path, with the `+podman+` suite of tools.
By importing this container stack, FreeBSD users both benefit from common tooling, but also enjoy wide support across public and private container registries, and container-specific tooling and services.
-In the https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest}-RELEASE/aarch64/Latest/[aarch64] and https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest}-RELEASE/amd64/Latest/[amd64] download directories, you'll see official OCI-format images.
+In the https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest-next}-RELEASE/aarch64/Latest/[aarch64] and https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest-next}-RELEASE/amd64/Latest/[amd64] download directories, you'll see official OCI-format images.
The naming may be a little confusing at first, but should make sense once you start using them.
The same images are also available through common public container registries, including https://hub.docker.com/u/freebsd[Docker Hub], and https://github.com/orgs/freebsd/packages[Github Container Registry], but for the strongest chain of trust, you should fetch your image directly from https://download.freebsd.org/releases[Official FreeBSD Releases], and import them to your local system.
* `+freebsd-runtime:14.snap+` - always the latest build from 14-STABLE branch
* `+freebsd-runtime:15.snap+` - always the latest build from 15-STABLE branch
* `+freebsd-runtime:16.snap+` - always the latest build from 16-CURRENT branch
+
+[[containers-registries]]
+== Official Registry Locations
+
+Architecture-independent images can be downloaded and directly imported into your own registry from the https://download.freebsd.org/releases[Official FreeBSD Releases] site, or alternatively, pulled from the FreeBSD project's https://hub.docker.com/u/freebsd[Docker Hub], or https://github.com/orgs/freebsd/packages[GitHub Container Registry].
+
+Both https://hub.docker.com/_/alpine/[Alpine Linux] and the https://hub.docker.com/_/debian/[Debian] project publish tagged and mutable images, using similar approaches.
+
+[[containers-podman-intro]]
+== Introduction to Podman
+
+The Podman suite comprises three main tools and a number of supporting ones:
+
+* https://podman.io/[podman] is a daemonless container engine using docker-compatible commands
+* https://github.com/containers/skopeo[skopeo] works with remote OCI image registries
+
+[[containers-preparing]]
+== Preparing the System for Podman Containers
+
+[WARNING]
+====
+If running commands over SSH, or in a similar remote session, use man:screen[1], man:tmux[1], or similar to avoid losing the connection while restarting the firewall.
+====
+
+If ZFS is not available, switch to UFS storage and skip the ZFS commands below:
+Amend `+/etc/sysctl.conf+` and `+/etc/fstab+` as appropriate, to make these changes permanent.
+
+[[containers-installing]]
+== Installing Podman
+
+Only the `+sysutils/podman-suite+` meta-package is required, but if the additional `+emulators/qemu-user-static+` package is installed, it becomes possible to build or test images for other architectures such as `+arm64+` on an `+amd64+` host.
+Integrate changes from `+/usr/local/etc/containers/pf.conf.sample+` into `+/etc/pf.conf+`, setting egress macros appropriately, then restart the firewall:
+
+[source,shell]
+----
+# service pf restart
+----
+
+The packages install a number of template configuration files, none of which need to be edited immediately.
+Review and amend these as needed:
+
+[source,shell]
+----
+# pkg list buildah podman conmon \
+ ocijail containers-common \
+ containernetworking-plugins \
+ | grep /etc/
+/usr/local/etc/containers/containers.conf.sample
+/usr/local/etc/containers/policy.json.sample
+/usr/local/etc/containers/registries.conf.sample
+/usr/local/etc/containers/storage.conf.sample
+/usr/local/etc/containers/pf.conf.sample
+----
+
+[[containers-terminal-tour]]
+== Importing and Running Containers
+
+[NOTE]
+====
+At present, all Podman containers on FreeBSD must run as root, as FreeBSD jails require this.
+====
+
+With the necessary tools and firewall rules in place, the officially published images can be fetched from https://download.freebsd.org/releases/OCI-IMAGES/[FreeBSD Releases], for https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest-next}-RELEASE/aarch64/Latest/[aarch64] or https://download.freebsd.org/releases/OCI-IMAGES/{rel-latest-next}-RELEASE/amd64/Latest/[amd64] as appropriate.
+
+[[containers-importing]]
+=== Importing FreeBSD OCI Images
+
+It is simplest to pull images directly from a public container registry, but for the strongest chain of trust, download them from https://download.freebsd.org/releases[FreeBSD.org] directly, and verify the checksums against the PGP-signed release announcement.
+localhost/freebsd-toolchain {rel-latest-next}-RELEASE-amd64 b927c11b2e8d 13 days ago 660 MB
+localhost/freebsd-notoolchain {rel-latest-next}-RELEASE-amd64 086572f35cc9 13 days ago 152 MB
+localhost/freebsd-runtime {rel-latest-next}-RELEASE-amd64 90c493675429 13 days ago 34.1 MB
+localhost/freebsd-dynamic {rel-latest-next}-RELEASE-amd64 33427d59f990 13 days ago 14.5 MB
+localhost/freebsd-static {rel-latest-next}-RELEASE-amd64 5921c62e76c1 13 days ago 2.92 MB
+----
+
+Note that the image IDs are reproducible each time, and the `+IMAGE_ID+` column matches the hashes reported when importing the images.
+
+[[containers-listing-layers]]
+=== Listing Layers
+
+It is possible to show the layers that comprise an image, demonstrating how the `+runtime+` image has three components, each one a layer in its own right:
+
+[source,shell]
+----
+# podman image tree ghcr.io/freebsd/freebsd-runtime:14.2
+Image ID: c5f3e77557a9
+Tags: [ghcr.io/freebsd/freebsd-runtime:14.2]
+Size: 35.07MB
+Image Layers
+├── ID: cd53fb07fb66 Size: 5.449MB Top Layer of: [ghcr.io/freebsd/freebsd-static:14.2]
+├── ID: a01d37f7777b Size: 10.4MB Top Layer of: [ghcr.io/freebsd/freebsd-dynamic:14.2]
+└── ID: 36b0c80ca1f7 Size: 19.21MB Top Layer of: [ghcr.io/freebsd/freebsd-runtime:14.2]
+----
+
+[[containers-public-registries]]
+=== Using Public Registries
+
+While the most secure provenance is downloading from https://download.freebsd.org/releases[Official FreeBSD Releases], there are two public container registries managed by the FreeBSD Release and Cluster Admin teams.
+The images from both registries are identical, but provide a simpler and more container-friendly workflow.
+As the container image does not include the kernel, man:freebsd-version[1] shows different results for the running kernel versus the installed userland.
+In this example, a {rel-latest}-RELEASE container runs on a {rel-latest-next}-RELEASE host:
+
+[source,shell,subs="attributes"]
+----
+# podman run -it --rm ghcr.io/freebsd/freebsd-notoolchain:{rel-latest} /bin/sh
+# freebsd-version -ru
+{rel-latest-next}-RELEASE
+{rel-latest}-RELEASE
+# exit
+----
+
+[[containers-variations]]
+== Useful Variations and Tips
+
+Almost all of the `+podman run ...+` parameters also apply to `+podman build+`.
+Refer to the man:podman-run[1] and man:podman-build[1] manual pages for details.
+
+Use the `+--rm+` flag to have ephemeral containers clean themselves up afterwards.
+
+Use `+podman images -a+` to show all downloaded images:
+
+[source,shell,subs="attributes"]
+----
+# podman images -a
+REPOSITORY TAG IMAGE ID CREATED SIZE
+ghcr.io/freebsd/freebsd-runtime {rel-latest-next} 90c493675429 2 weeks ago 34.1 MB
+----
+
+Use `+podman ps -a+` to see running containers:
+
+[source,shell,subs="attributes"]
+----
+# podman ps -a
+CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
+3123623bef9b ghcr.io/freebsd/freebsd-runtime:{rel-latest-next} /bin/sh 4 minutes ago Up 4 minutes admiring_brattain
+----
+
+[[containers-env-vars]]
+=== Environment Variables
+
+Podman sets a number of default environment variables in each container:
+
+[source,shell,subs="attributes"]
+----
+# podman run -it --rm ghcr.io/freebsd/freebsd-notoolchain:{rel-latest-next} /usr/bin/env
+For a lean image such as `+freebsd-static+`, mount the existing man:pkg-static[8] from the host to bootstrap man:pkg[8] inside the container.
+Sharing the host's package cache via `+--volume /var/cache/pkg+` avoids re-downloading packages that have already been fetched, which is particularly useful when building multiple containers.
+The `+--no-hosts+` flag prevents mounting `+/etc/hosts+`, which would otherwise interfere with package upgrades.
+
+[NOTE]
+====
+Sharing the package cache only works when the host and container run the same FreeBSD major version, as packages are version-specific.
+The container images do not include man:pkg[8] itself, but man:pkg-static[8] can be mounted from the host to query the installed base system packages.
+Use `+pkg info -q+` to list package names, or `+pkg info -ql+` to show all files owned by packages:
+
+[source,shell,subs="attributes"]
+----
+# podman run --rm \
+ -v /usr/local/sbin/pkg-static:/pkg \
+ ghcr.io/freebsd/freebsd-runtime:{rel-latest-next} /pkg info -q
+FreeBSD-audit-lib-{rel-latest-next}
+FreeBSD-bzip2-lib-{rel-latest-next}
+FreeBSD-certctl-{rel-latest-next}
+FreeBSD-clibs-{rel-latest-next}
+FreeBSD-fetch-{rel-latest-next}
+FreeBSD-kerberos-lib-{rel-latest-next}
+FreeBSD-libarchive-{rel-latest-next}
+FreeBSD-libcasper-{rel-latest-next}
+FreeBSD-libexecinfo-{rel-latest-next}
+FreeBSD-libucl-{rel-latest-next}
+FreeBSD-mtree-{rel-latest-next}
+FreeBSD-ncurses-lib-{rel-latest-next}
+FreeBSD-openssl-{rel-latest-next}
+FreeBSD-openssl-lib-{rel-latest-next}
+FreeBSD-pam-lib-{rel-latest-next}
+FreeBSD-pkg-bootstrap-{rel-latest-next}
+FreeBSD-rc-{rel-latest-next}
+FreeBSD-runtime-{rel-latest-next}
+FreeBSD-xz-lib-{rel-latest-next}
+FreeBSD-zlib-{rel-latest-next}
+FreeBSD-zoneinfo-{rel-latest-next}
+FreeBSD-zstd-lib-{rel-latest-next}
+----
+
+When the host and container run different FreeBSD major versions, man:pkg[8] detects the ABI by inspecting `+/bin/sh+` inside the container, and will warn about the mismatch:
+pkg: Warning: Major OS version upgrade detected. Running "pkg bootstrap -f" recommended
+----
+
+For images without `+/bin/sh+`, such as `+freebsd-dynamic+`, man:pkg-static[8] cannot detect the system ABI at all.
+
+[[containers-networking]]
+== Networking and Name Resolution
+
+By default, the Podman suite of tools will use man:mount_nullfs[8] to mount `+/etc/resolv.conf+` and a modified `+/etc/hosts+` from the jail host, as well as a `+.containerenv+` file in `+/var/run+`.
+
+This hosts file will conflict with base system package upgrades, so it is advised to skip this during `+podman build+` runs, but leave it enabled during production deployment:
+
+[source,shell]
+----
+# podman run --dns 1.2.3.4 ...
+# podman build --no-hosts ...
+----
+
+The `+--dns+` flag specifies a custom DNS server instead of mounting `+/etc/resolv.conf+` from the host.
+The `+--no-hosts+` flag prevents mounting `+/etc/hosts+` from the host.
+
+[[containers-building]]
+== Building Custom Images
+
+Custom images can be built directly from tarballs (for example those from man:poudriere[8], or official release tarballs), from existing tagged images, or bootstrapped from man:pkg[8].
+
+[[containers-building-base]]
+=== Building from Tarballs
+
+A custom image can be made from any FreeBSD root filesystem tarball using man:podman-import[1].
+The tarball can be an official release `+base.txz+`, one produced by man:poudriere-image[8], or your own custom tarball.
+The final tagged image can be pushed to registries, modified, and used for further image creation.
+Run the web server in the background with `+-d+`, and use `+-p+` to map a host port to the container port.
+In this example, the container listens on its default port 8000, which is mapped to port 8888 on the host:
+
+[source,shell,subs="attributes"]
+----
+# c=$(podman run -d --rm -p 8888:8000 localhost/python-www:{rel-latest-next})
+----
+
+The port mapping uses man:pf[4] redirect rules.
+These rules redirect traffic arriving over the network, so the mapped port must be accessed from another host, or via the host's network-facing IP address:
+
+[source,shell]
+----
+$ fetch -v http://172.16.2.10:8888/
+resolving server address: 172.16.2.10:8888
+requesting http://172.16.2.10:8888/
+remote size / mtime: 784 / 0
+fetch.out 784 B 3791 kBps 00s
+----
+
+To access the container directly from the host, use the container's own IP on its listening port:
+Port mappings via `+-p+` are handled by man:pf[4] redirect rules and only apply to traffic arriving over the network.
+Traffic originating from the jail host itself to `+localhost+` or `+127.0.0.1+` will not be redirected.
+From the host, use the container's IP address directly instead.
+====
+
+[[containers-advanced]]
+== Advanced Usage
+
+[[containers-daemonising]]
+=== Daemonising Podman
+
+FreeBSD's Podman implementation provides two man:rc[8] services:
+
+* The `+podman+` service runs at boot and ensures that all containers marked with `+restart-policy=always+` are started.
+* The `+podman_service+` service runs the https://docs.podman.io/en/latest/_static/api.html[Podman API service], providing a REST API and listening on `+/var/run/podman/podman.sock+` by default.
+
+Enable them as follows:
+
+[source,shell]
+----
+# sysrc podman_enable=YES
+# sysrc podman_service_enable=YES
+# sysrc podman_service_flags='--time 0'
+# service podman start
+# service podman_service start
+----
+
+Containers that should restart automatically after a host reboot must be run with the `+--restart=always+` option:
+
+[source,shell]
+----
+# podman run -d --restart=always my-image
+----
+
+[[containers-linux]]
+=== Running Linux Containers
+
+It is possible to run many Linux container images using FreeBSD's Linux emulation:
+
+[source,shell]
+----
+# service linux onestart
+# podman run --rm --os=linux docker.io/alpine cat /etc/os-release | head -1
+Trying to pull docker.io/library/alpine:latest...
+Getting image source signatures
+Copying blob 4abcf2066143 done |
+Copying config 05455a0888 done |
+Writing manifest to image destination
+NAME="Alpine Linux"
+----
+
+[NOTE]
+====
+Linux containers that depend on systemd, expect to be PID 1, or rely on Linux-specific kernel interfaces may not work correctly under FreeBSD's Linux emulation.
+====
+
+[[containers-local-registry]]
+=== Running a Local Registry
+
+A registry is a web server for storing and sharing OCI images.
+The https://freshports.org/sysutils/docker-registry[sysutils/docker-registry] port provides a simple registry suitable for local development or a small organisation.
+https://zotregistry.dev/[Zot Registry] is a more comprehensive modern alternative.