Podman primer
Yet another post for my future self, this time documenting how I currently setup podman containers on my overly complicated home network.
It’s worth noting that I’m using the version of podman (3.4.4) shipped with Ubuntu 22.04, and that version 4 is supposed to be a lot better in some way. I haven’t had any real issues with my setup outlined here so I have been too lazy to try to upgrade. :)
Creating networks #
I have multiple networks in my setup – one for iot, one for management, one for my webserver…all due to reasons. I’m using vlans to separate these networks, and send tagged traffic on my main interface.
In most cases I want my containers to live on their designated vlan
and not on a default host switch. For this I use macvlan
interfaces, and base these subinterfaces on the bridge name.
podman network create \
-d macvlan \
-o parent=br-network \
--subnet 192.0.2.0/24 \
--gateway 192.0.2.1 \
--ip-range 192.0.2.0/24 \
examplenet
I’m not sure it’s correct to hang these on each bridge, on FreeBSD I’d just create a bridge, add the vlan subinterface and then add each epair to the bridge.
Creating secrets #
I’ve been bitten multiple times by line endings in secrets, as I’ve configured my editors to include one at the end by default. The best way to create secrets is to use printf like below.
printf "supersecret" | podman secret create example-secret -
You can replace the dash by a file path instead, but be careful with these line endings.
Create container #
I use create and not run, letting systemd manage starting the container. As I typically use macvlan I don’t specify ports as it’s meaningless in that case.
podman create \
--name example \
--ip=192.0.2.3 \
--network examplenet \
-e TZ=Europe/Stockholm \
--secret example-secret,type=env,target=EXAMPLE_SECRET \
-v "/tmp:/tmp" \
--memory 128M \
--cpus=1 \
hello-world
Create systemd service #
Using --new
when generating the systemd service file makes systemd
recreate it on each start. It’s necessary to use if you want
podman auto-update to work.
podman generate systemd --new example > /etc/systemd/system/container-example.service
systemctl daemon-reload
systemctl enable --now container-example.service
Auto update containers #
You need to add a label (at container creation) for each container you
want to auto update. If you forgot to, but used --new
with podman generate systemd
, then you can just add the label in the service file.
podman create --label io.containers.autoupdate=registry ...
To manually check if your containers have updates (remove --dry-run
for doing the update):
podman auto-update --dry-run
I’m not sure if podman-auto-update.timer
is enabled by default, but
it might be. Otherwise that timer will run once every 24h and update,
restart and rollback (if necessary) your containers.
Some examples #
Here are a few containers I’m currently using with podman at home:
- postgres14
- grafana-oss
- navidrome
- radicale
- loki
- promtail
- miniflux
- restic-restserver
- caddy
- drone
- drone-runner
- omada-controller
You might notice that I don’t export any ports and assign all IPs manually. Each container effectively has their own networking namespace.
drone-runner #
This one makes me nervous, as I’m effectively giving this container root on my host system. :-)
podman create \
--label io.containers.autoupdate=registry \
--name runner \
--ip=192.0.2.5 \
--dns=192.0.2.2 \
--network foobarnet \
--secret drone_gitea_rpc_secret,type=env,target=DRONE_RPC_SECRET \
--secret drone_server_host,type=env,target=DRONE_RPC_HOST \
--secret drone_server_proto,type=env,target=DRONE_RPC_PROTO \
-e DRONE_RUNNER_CAPACITY=1 \
-e DRONE_RUNNER_NAME="runner" \
--memory 2048M \
--cpus=2 \
-v /run/podman/podman.sock:/var/run/docker.sock \
docker.io/drone/drone-runner-docker
It seems drone-runner is fine to use the podman socket instead of the default docker socket.
restic-restserver #
Nothing special here, just another example.
podman create \
--label io.containers.autoupdate=registry \
--name rest_server \
--ip=192.0.2.10 \
--dns=192.0.2.2 \
--network baznet \
-v "/path/to/restic/:/data" \
-e OPTIONS="--private-repos --listen :8000 --append-only --prometheus" \
--cpus=1 \
--memory 1G \
restic/rest-server
radicale #
I just copied this from dockerhub. :-)
podman create \
--label io.containers.autoupdate=registry \
--name radicale \
--ip=192.0.2.15 \
--dns=192.0.2.2 \
--network abcnet \
--init \
--read-only \
--cap-drop ALL \
--cap-add CHOWN \
--cap-add SETUID \
--cap-add SETGID \
--cap-add KILL \
--pids-limit 50 \
--memory 256M \
--cpus=1 \
--health-cmd="curl --fail http://localhost:5232 || exit 1" \
--health-interval=30s \
--health-retries=3 \
-v /path/to/radicale:/data \
tomsquest/docker-radicale