# Podman on FreeBSD FAQ
## Intro
This is a collection of commonly asked questions for users running Podman on FreeBSD.
It is an independent extension of Dave's comprehensive Podman documentation for FreeBSD [here](https://docs.skunkwerks.at/LqHthEkTSeGDwV0PDUQSyg).
## Ideology
### Why not use regular jails? I don't want to fragment the ecosystem
You can opt for regular jails, along with the various jail orchastration tools like `appjail`, `bastille`, `iocage`, `pot`, or roll your own with the inbuilt jail toolset.
However, if you want small, prebuilt images, hosted by a third party like `docker.io` or `ghcr.io`, which can also be used in a `podman-compose` setup, then OCI images are a good fit.
## Networking
### How do I add ipv6 to my containers?
You can create a second podman network with the `ipv6` flag set as follows
```
doas podman network create NETWORK-NAME --ipv6
```
The default `ipv4` range will be `10.89.0.0/24` and the default `ipv6` range will be `fdbe:8dcb:624e:be::/64` on interface `cni-podman1`
> The default podman network will still be using `ipv4` range of `10.88.0.0./16` on interface `cni-podman0`.
Make sure to update `/etc/pf.conf` to reflect the IPv6 range (see below) and make sure it matches either the default IPv6 range, or the custom IPv6 range you set, or as set in a `podman-compose.yml` network setup.
To make a container use the new named network with `ipv6` when building an image, use
```
doas podman build --network NETWORK-NAME -t TAG -f Containerfile
```
To make a container user the new named network with `ipv6` when running an image, use
```
doas podman run -dt \
--name=NAME \
--network=NETWORK-NAME \
--restart=always \
your-container/image:latest
```
### Can I create all the network types listed in `man podman-network-create`?
No. While the man page for `podman-network-create` lists
```
--driver, -d=driver
Driver to manage the network. Currently bridge, macvlan and ipvlan are
supported. Defaults to bridge. As rootless the macvlan and ipvlan
driver have no access to the host network interfaces because rootless
networking requires a separate network namespace
```
developer feedback is
>"there's no kernel support for rootless jails yet, so macvlan and ipvlan are out."
### My containers have no inter-container DNS resolution
There is no `dnsname` or `aardvark-dns` plugin support available at this time.
A workaround for containers being unable to resolve other containers by name exists as outlined here.
You must manually set the container IP address, and pass in other container `hostname:ip` mappings with the `--add-host` parameter, for example:
```
podman run -dt \
--ip=1.2.3.4 \
--name=NAME \
--hostname={ hostname } \
--add-host={{ container2 }}:{{ 1.2.3.5 }} \
--add-host={{ container3 }}:{{ 1.2.3.6 }} \
--add-host={{ container4 }}:{{ 1.2.3.7 }} \
NAME:latest
```
If using `podman compose` your `podman-compose.yml` can use the following layout:
```
services:
container1:
build: ./container1/
ports:
- "host-ipv4-address:1234:1234"
- "host-ipv6-address:1234:1234"
networks:
mynetwork:
ipv4_address: 10.89.0.5
ipv6_address: fdca:1db3:f328:2d7c::5
extra_hosts:
- "container2:10.89.0.6"
- "container3:10.89.0.7"
- "container4:10.89.0.8"
volumes:
- my_data:/var/db/data
restart: always
<snip other containers>
networks:
mynetwork:
driver: bridge
ipam:
config:
- subnet: "10.89.0.0/24"
gateway: "10.89.0.1"
- subnet: "fdca:1db3:f328:2d7c::/64"
gateway: "fdca:1db3:f328:2d7c::1"
```
### Can I use 127.0.0.1 for inter-container communication?
Yes, according to [this documentation](https://github.com/containers/podman/blob/main/docs/tutorials/basic_networking.md#communicating-between-containers-and-pods):
```
By definition, all containers in a Podman pod share the same network namespace.
This fact means that they will have the same IP address, MAC addresses,
and port mappings.
You can conveniently communicate between containers in a pod by using localhost.
```
As long as two containers don't try use the same port number, you can have one container use 127.0.0.1:80 and another use 127.0.0.1:8080 and each container can see the other's service on the respective port.
### My single container in podman-compose file has IP address conflict or similar
You must have an `extra_hosts:` option in your podman-compose.yml file when setting custom networking. Failure to include at least one entry may result in errors like:
```
Error: unable to start container "eea53eca213ec2f32f4abba72e0a75e5ae89ce483e301ef5a6edf94b0bc48aed": plugin type="bridge" failed (add): cni plugin bridge failed: failed to allocate for range 0: 10.89.0.4 has been allocated to eea53eca213ec2f32f4abba72e0a75e5ae89ce483e301ef5a6edf94b0bc48aed, duplicate allocation is not allowed
```
or
```
Error: unable to start container "37f78c6f83dd186e39c7a6f453aa2cd4d43c69f2a973eb795270a4f598acae9e": plugin type="bridge" failed (add): cni plugin bridge failed: failed to allocate for range 0: requested IP address 10.89.0.3 is not available in range set 10.89.0.1-10.89.0.254
```
The container will start normally if you add an `extra_hosts:` option to your `podman-compose.yml` file, for example:
```
extra_hosts:
- "gw:10.89.0.1"
```
For podman-compose setups with multiple containers this would normally be added for internal DNS, but for setups with only a single container it would be excluded as no need for internal DNS. That's incorrect, you must have at least one entry.
## Build
### Podman compose is looking for a Dockerfile but I have Containerfile
If using `podman compose up -d` and you get an error like the following:
```
OSError: Dockerfile not found in /path/to/app/
Error: executing /usr/local/bin/podman-compose up -d: exit status 1
```
It's slightly misleading because it's looking for `Dockerfile` or `Containerfile` and the problem is likely an error in the path in your `podman-compose.yml` file
Check the path in the build section of `podman-compose.yml` and verify it matches the path in the directory
```
services:
app:
build: ./app/
```
### My built containers are large with too many layers
If you have large containers, or too many layers, it might be because you're installing packages over multiple lines, for example your `Containerfile` might have:
```
RUN ASSUME_ALWAYS_YES=yes pkg install -y openssl
RUN ASSUME_ALWAYS_YES=yes pkg install -y ca_root_nss
RUN ASSUME_ALWAYS_YES=yes pkg install -y curl
RUN ASSUME_ALWAYS_YES=yes pkg install -y jq
RUN ASSUME_ALWAYS_YES=yes pkg install -y grafana
```
It seems it's better to handle all package installs in a single layer by using a single line as follows
```
RUN ASSUME_ALWAYS_YES=yes pkg install -y openssl \
ca_root_nss \
curl \
jq \
grafana
```
This should reduce the number of layers in an image with many packages installed.
### My containers won't run some apps due to missing libraries
If using the `freebsd-runtime` container image as source, you may get missing library errors.
Try using the `freebsd-notoolchain` container image instead. It's only slightly larger.
If still no luck, build a container from `base.tgz` as covered in next item.
### How can I create a container from `base.txz`?
Create an OCI jail from `base.tgz` with the following
```
doas podman import --os freebsd \
--arch amd64 \
--message 'Import FreeBSD 14.3 base.txz' \
https://download.freebsd.org/ftp/releases/amd64/14.3-RELEASE/base.txz
```
Then run `podman image ls` to get container ID, then tag the container with
```
podman image tag <container-id> localhost/freebsd-custom:14.3
```
> note: linked [docs](https://docs.skunkwerks.at/LqHthEkTSeGDwV0PDUQSyg#daemonising) use `arm64`, this example uses `amd64`
### My containers won't complete 'pkg bootstrap' step
1. Do you have two container images with the same image id?
If you installed a container with
```
doas podman load -i=https://download.freebsd.org/releases/OCI-IMAGES/14.2-RELEASE/amd64/Latest/FreeBSD-14.2-RELEASE-amd64-container-image-minimal.txz
```
and then did
```
doas podman pull docker.io/freebsd/freebsd-runtime:14.2
```
they will have the same image id and cause pkg bootstrap errors.
remove both at once with
```
doas podman rmi <image-id> --force
```
Only use a single source for the same image.
2. Check your pf.conf configuration in dualstack setups
In a dualstack setup, the following `pf.conf` works, but the documented one does not, and `pkg bootstrap` may fail. You can get around a NAT hairpin issue by including `rdr-anchor "cni-rdr/*" on lo0`.
```
v4egress_if = "vtnet0"
v6egress_if = "vtnet0"
cni_if0 = "cni-podman0"
cni_if1 = "cni-podman1"
table <cni-nat> persist { 10.88.0.0/16, 10.89.0.0/24, fdca:1db3:f328:2d7c::/64 }
nat on $v4egress_if inet from <cni-nat> to any -> ($v4egress_if)
nat on $v6egress_if inet6 from <cni-nat> to !ff00::/8 -> ($v6egress_if)
rdr-anchor "cni-rdr/*"
nat-anchor "cni-rdr/*"
nat-anchor "cni-nat/*"
anchor "cni-nat/*"
# Pass rules so packets aren’t blocked
pass quick on $cni_if0 inet from ($cni_if0:network) to any keep state
pass quick on $cni_if1 inet from ($cni_if1:network) to any keep state
pass quick on $cni_if0 inet6 from ($cni_if0:network) to any keep state
pass quick on $cni_if1 inet6 from ($cni_if1:network) to any keep state
pass out quick on $v4egress_if inet from ($cni_if0:network) to any keep state
pass out quick on $v4egress_if inet from ($cni_if1:network) to any keep state
pass out quick on $v6egress_if inet6 from ($cni_if0:network) to any keep state
pass out quick on $v6egress_if inet6 from ($cni_if1:network) to any keep state
# ICMP/ICMPv6 (ping + PMTU)
pass inet proto icmp from ($cni_if0:network) to any keep state
pass inet proto icmp from ($cni_if1:network) to any keep state
pass inet6 proto icmp6 from ($cni_if0:network) to any keep state
pass inet6 proto icmp6 from ($cni_if1:network) to any keep state
```
**NB:** Make sure the IPv4 range covers default `podman` network and IPv6 range covers the range allocated to your second named network. It may differ from the default set when adding a second network with IPv6, such as automation in a `podman-compose.yml` file.
### How do I make containers persistent
Podman can be configured to auto-start containers at startup if the `podman` service is enabled; rc.d instructions [here](https://docs.skunkwerks.at/LqHthEkTSeGDwV0PDUQSyg#daemonising).
#### With podman run
Simply include the argument `restart=always`
```
doas podman run -d --restart=always your-container/image
```
#### With podman compose
Include a similar parameter in `compose.yaml`
```
services:
web:
image: your-container-image
ports:
- 9932:5432
restart: always
```
### My containers aren't restarting on reboot
First make sure to have the `podman` service enabled:
```
service podman enable
```
If your containers still won't restart on reboot it's likely because your restart policy isn't set to `always`.
The `podman` RC service on FreeBSD is only evaluating containers that explicitly have a restart policy of `always`.
A restart policy of `unless-stopped` will not match and the container won't be restarted.
> Note: there is a `podman` service and a `podman_service` service. The `podman` service needs to be enabled to automatically restart containers.
### Ran out of space for builds
You may get an error running `podman build` like this
```
ApplyLayer stdout: stderr: write /var/db/pkg/repos/FreeBSD/db: no space left on device exit status 1
```
Expand the zpool. You can do this by either:
1. Adding new disks to the pool.
2. Resizing the disk used by the pool (if running in a VM).
Actualize new pool size:
1. `zpool set autoexpand=on zroot`
2. `zpool online -e zroot vtbd0p4`
Replace `zroot` and `vtbd0p4` as necessary.
## Operation
### How do I add persistent storage to my container
You can add persistent storage to a container with a volume mount during the run step.
```
doas podman run -dt \
--name=NAME \
--volume "/ext/path/to/dir:/int/path/to/dir:rw" \
--restart=always \
your-container/image:latest
```
For example if you wanted to mount /mnt/data/mywebsite into /usr/local/www/mywebsite inside the container
```
doas podman run -dt \
--name=NAME \
--volume "/mnt/data/mywebsite:/usr/local/www/mywebsite:rw" \
--restart=always \
your-container/image:latest
```
### I have a number of untagged images
If the output of `doas podman image ls` has one or more untagged images, such as from failed build, you can clean up with
```
doas podman image prune
```
And choose `y` when prompted. All dangling container images will be removed.
### How do I save container logs to a file?
On FreeBSD the log file format defaults to `k8s-file`. This is not a JSON log as podman documentation suggests, but a regular syslog file.
To have the logs saved somewhere specific, create a directory like `/var/log/containers` and then configure your `podman run` command with the `--log-opt path=path` parameter as follows:
```
doas podman run -dt \
--name=NAME \
--volume "/mnt/data/mywebsite:/usr/local/www/mywebsite:rw" \
--log-opt path=/var/log/containers/mywebsite.log \
--restart=always \
your-container/image:latest
```
or set your `podman-compose.yml` to configure external log file as follows
```
name:
...
logging:
driver: k8s-file
options:
path: /var/log/containers/mywebsite.log
```
Then tail the file `/var/log/containers/mywebsite.log` to see logs in real time.
### How do I limit container log file size?
Some services might generate a lot of log entries and cause the container dataset in `/var/db/containers/...` to fill up the available disk capacity.
Set a limit on container log file size in one of the following ways
For `podman-compose.yml` configure
```
name:
...
logging:
driver: k8s-file
options:
max-size: "10m"
```
or
```
podman run -d --log-driver k8s-file --log-opt max-size=10mb --name name myimage
```
or configure a max log file size in containers.conf
```
[containers]
log_size_max = 10485760 # Size must be in bytes (e.g., 10MB = 10485760)
```
The limit should also work when logging to external file as covered in previous question.
### How do I run postgresql containers with podman?
Short answer: You can't, this isn't automatic yet. Zabbix will also fail.
Longer answer #1: FreeBSD is now part of the OCI spec, so in time it should be possible for podman to enable `sysvmsg`, `sysvsem` and `sysvsha`, and specific jail options like allow flags. This is being worked on. Refer to [Conversion in github issue](https://github.com/dfr/ocijail/issues/3).
Longer answer #2: It is possible to enable the required options by using `oci-hook` as outlined in replies in this [mastodon post](https://infosec.exchange/@david_chisnall/114488278097933797).
Or more specifically, as posted by Dave Cottlehuber to the `freebsd-enterprisewg` mailing list on 27 November 2025, it is possible as follows:
1. Create the file `/usr/local/etc/containers/hooks.d/postgresql.json` with contents:
```
{
"version": "1.0.0",
"hook": {
"path": "/usr/local/etc/containers/hooks.d/postgresql.sh"
},
"when": {
"always": true
},
"stages": [
"createRuntime"
]
}
```
2. Create the file `/usr/local/etc/containers/hooks.d/postgresql.sh` with contents:
```
#!/bin/sh -e
set -o pipefail
INPUT=$( cat - | tee -a /var/log/postgresql.json)
ID=$( echo $INPUT | jq -r .id || exit 1)
STATUS=$( echo $INPUT | jq -r .status || exit 1)
# if we are in created state, we can proceed
if [ "$STATUS" == "created" ]; then
/usr/sbin/jail -vm name=$ID sysvmsg=1 sysvsem=1 sysvshm=1
logger -t oci postgresql hook for container $ID
fi
```
3. Make sure it's executable:
```
chmod +x /usr/local/etc/containers/hooks.d/postgresql.sh
```
4. Make sure the file `/usr/local/etc/containers/containers.conf` has the hooks section enabled, as per [Using Podman hooks to mount persistent ZFS datasets into ephemeral Containers](https://people.freebsd.org/~dch/posts/2025-06-27-oci-zfs/) post:
```
[engine]
hooks_dir = "/usr/local/etc/containers/hooks.d"
```
5. Restart the `podman_service` service [??]
Postgresql containers should work with this setup.