Five Things I Learned Building a Rootless Podman Server with Quadlet

Between August and November 2024, moving a small self-hosted server to rootless Podman was less about translating container definitions and more about discovering what each layer assumed: the distribution package versions, the network backend, port permissions, image storage, and user-namespace mappings. Services run under a dedicated rootless service account, with systemd user units generated from Quadlet and provisioned through Ansible. The eventual design used named volumes and Quadlet files.

This is not a deployment recipe. It is a record of the defaults that surprised me and the checks I would make before building the next server.

1. Treat distribution package versions as architecture

Symptom. The packaged Compose workflow on Debian 12 could not express the configuration I needed: podman-compose 1.0.x did not support Compose include.

Diagnosis. A container design depends on more than the image runtime. It also depends on the feature set shipped by the distribution: the Compose wrapper, networking components, and Quadlet support all evolve independently.

Fix. I temporarily installed a newer podman-compose in a Python virtual environment for that configuration. The project then introduced Debian 13/Trixie and Podman 5 before adopting Quadlet.

The repository history does not preserve the exact Podman capability behind the Debian 13 move, so I would not retrofit a confident explanation. The durable lesson is simpler: check the exact package versions and required features before committing to a deployment model.

Takeaway. Write down the Podman, Quadlet, and Compose features your design requires, then verify that the selected distribution ships them.

2. Make the rootless network backend explicit

Symptom. Containers could not reliably communicate with each other.

Diagnosis. The network configuration did not provide reliable container-to-container communication until the intended backend was selected. Older state can retain legacy CNI configuration, making backend selection dependent on the machine's history rather than configuration alone.

Fix. I selected Netavark explicitly:

[network]
network_backend = "netavark"

Containers must be stopped before changing network backends. Stale interfaces or iptables rules may also require a reboot before the new backend takes effect.

Takeaway. Select the rootless network backend before accumulating state, then test service-name resolution between containers as well as host-published ports.

3. Rootless low-port binding needs an explicit host policy

Symptom. A rootless service needed a low port, but attempting to grant file capabilities did not work.

Diagnosis. Rootless container isolation and a host's privileged-port policy are separate mechanisms.

Fix. I made all ports unprivileged on that host:

net.ipv4.ip_unprivileged_port_start = 0

This is a host-wide policy choice: it allows any unprivileged process to bind ports below 1024. It is not automatically the right answer for every server.

Takeaway. Decide early whether low ports will use an unprivileged-port sysctl or another explicit host policy.

4. Inspect rootless image storage before the disk fills

Symptom. The rootless VFS storage driver used far more disk space than was practical.

Diagnosis. The storage driver itself was a capacity concern.

Fix. I configured rootless storage to use OverlayFS through fuse-overlayfs:

[storage]
driver="overlay"
[storage.options]
mount_program="/usr/bin/fuse-overlayfs"

Takeaway. Check the storage driver and measure it before the server pulls its full image set. Storage behavior is an architectural choice, not a cleanup task for later.

A secure image is not a lifecycle guarantee

I initially chose Bitnami images partly because their non-root-oriented defaults and hardened application images reduced the amount of container hardening I had to assemble myself. VMware had acquired Bitnami, but I do not treat that transaction as proof of why the later catalog policy changed. The policy itself was clear: from 28 August 2025, Bitnami stopped generating Debian-based public images, kept only a limited hardened/latest-tag community tier, and moved older or versioned images to bitnamilegacy without updates or support.

For an existing user, that was a serious vendor-induced migration: I moved the affected images to bitnamilegacy as a temporary bridge in September 2025, then migrated the services to official images in August 2026. The hard lesson is that a secure base image does not guarantee a sustainable image lifecycle. Before relying on an image supplier for persistent services, understand its update, tag-retention, and support policies.

Sources: VMware's 15 May 2019 acquisition announcement, a contemporaneous report on the completed acquisition, and Bitnami's August 2025 catalog announcement.

5. Persistent data needs stable UID and GID mappings

Symptom. An experiment with UserNS=auto gave a container a different UID mapping after a reboot.

Diagnosis. For persistent data, I treated a moving mapping as incompatible with predictable ownership.

Fix. I moved to planned fixed maps for each service:

UIDMap=0:<service-subuid-start>:<range-size>
GIDMap=0:<service-subgid-start>:<range-size>

At the time, the fixed-map commit referenced containers/podman#22803.

Takeaway. Choose and document the ID-allocation strategy before a service writes persistent data. Stable ownership is part of the storage design.

What remained after the experiments

The resulting server model was deliberately boring: named volumes hold service state, and the project had adopted Quadlet. A dedicated lingering service account runs the generated systemd user units, which Ansible manages through machinectl. It had introduced Debian 13/Trixie and Podman 5 before doing so.

Before the next deployment, I would verify these five things:

  1. The distribution ships the Podman, Quadlet, and Compose features the design needs.
  2. The intended rootless network backend is selected before durable state exists.
  3. The host has an explicit policy for low-port ingress.
  4. Rootless storage uses a driver whose disk behavior has been measured.
  5. Persistent services have stable, documented subordinate UID/GID mappings.

Comments Add one by emailing me.