Podman quadlets on openSUSE: your first rootless service
Write a .container file, let systemd generate the service, start it at boot, and debug it when it does not appear.
Part 8 of 10 in openSUSE home server setup. Last updated
In short: A quadlet is a small .container file in ~/.config/containers/systemd/ (rootless) or /etc/containers/systemd/ (rootful). After systemctl --user daemon-reload, podman's systemd generator turns whoami.container into whoami.service, which you start with systemctl --user start whoami. Boot start comes from the [Install] section in the file, not from systemctl enable. If the service does not appear, run the generator with --dryrun to see the error.
What a quadlet is
A quadlet is a systemd unit file for a container, minus the boilerplate. You write a short
.container file describing the image, ports and volumes; at every
daemon-reload, podman's systemd generator translates it into a full
.service. From then on systemd starts it at boot, restarts it on failure and
collects its logs, like any other service.
| Runs as | Directory | Commands |
|---|---|---|
| Your user (rootless) | ~/.config/containers/systemd/ | systemctl --user ... |
| root (rootful) | /etc/containers/systemd/ | sudo systemctl ... |
A first quadlet
As the container user from the rootless podman guide, no root needed:
mkdir -p ~/.config/containers/systemd
nano ~/.config/containers/systemd/whoami.container [Unit]
Description=whoami test web server
[Container]
Image=docker.io/traefik/whoami:latest
PublishPort=8080:80
AutoUpdate=registry
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
The file name becomes the service name: whoami.container turns into
whoami.service. WantedBy=default.target starts it when the user's
service manager starts, which with lingering means at boot. A tested version with notes is in
the whoami quadlet example.
Start, check, read the logs
systemctl --user daemon-reload # run the generator
systemctl --user start whoami
systemctl --user status whoami
curl http://localhost:8080 # prints request details
journalctl --user -u whoami -n 50 # the container's output To reach it from another machine, open 8080/tcp in firewalld (needs root), as shown in the firewalld basics guide. The systemctl and journalctl guide covers the rest of the service commands.
When the service does not appear
"Unit whoami.service not found" after a daemon-reload means the generator rejected the file. It logs why, but the quickest check is a dry run:
/usr/lib/systemd/user-generators/podman-user-generator --dryrun # rootless
sudo /usr/lib/systemd/system-generators/podman-system-generator --dryrun # rootful
podman quadlet list # podman 5.6 and newer (Tumbleweed) The dry run prints the generated service, or the line it choked on: a typo in a key, a key in the wrong section, or a file name without a known extension.
Both cases for real, with the generator pointed at a scratch folder through
QUADLET_UNIT_DIRS (paths shortened to the usual folder). A good file:
$ cat whoami.container
[Unit]
Description=whoami test web server
[Container]
Image=docker.io/traefik/whoami:latest
PublishPort=127.0.0.1:8080:80
AutoUpdate=registry
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
$ QUADLET_UNIT_DIRS=$PWD /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
quadlet-generator[3120683]: Loading source unit file ~/.config/containers/systemd/whoami.container
---whoami.service---
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=whoami test web server
SourcePath=~/.config/containers/systemd/whoami.container
RequiresMountsFor=%t/containers
[X-Container]
Image=docker.io/traefik/whoami:latest
PublishPort=127.0.0.1:8080:80
AutoUpdate=registry
[Service]
Restart=on-failure
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/usr/bin/podman rm -v -f -i systemd-%N
ExecStopPost=-/usr/bin/podman rm -v -f -i systemd-%N
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/usr/bin/podman run --name systemd-%N --replace --rm --cgroups=split --sdnotify=conmon -d --label io.containers.autoupdate=registry --publish 127.0.0.1:8080:80 docker.io/traefik/whoami:latest
[Install]
WantedBy=default.target Real output on openSUSE Tumbleweed, podman 6.0.2, 2026-09-28; user and host names and addresses redacted.
And one letter too many (PublishPorts=). The generator names the key and exits with 1 instead of 0:
$ QUADLET_UNIT_DIRS=$PWD /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
quadlet-generator[3120214]: Loading source unit file ~/.config/containers/systemd/typo.container
quadlet-generator[3120214]: converting "typo.container": unsupported key 'PublishPorts' in group 'Container' in ~/.config/containers/systemd/typo.container
quadlet-generator[3120214]: processing encountered some errors
$ QUADLET_UNIT_DIRS=$PWD /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun >/dev/null 2>&1; echo "exit=$?"
exit=1 Real output on openSUSE Tumbleweed, podman 6.0.2, 2026-09-28; user and host names and addresses redacted.
Updating the image
AutoUpdate=registry marks the container for podman's auto-update. Turn on the timer
once per user, and podman pulls newer images and restarts the service, rolling back if the new
one fails to start:
systemctl --user enable --now podman-auto-update.timer
podman auto-update --dry-run # what would be updated
Pin a version tag instead of latest for anything with a database: an unattended
major upgrade is not a fun way to start the day.
Common mistakes
systemctl --user enable whoami
Fails with a message about generated or transient units. The [Install] section
in the .container file does the enabling; edit it and daemon-reload.
Editing the file and only restarting
The generator runs at daemon-reload, not at restart. Edit, daemon-reload, then
restart.
Rewriting a whole compose file by hand
The compose to quadlet converter does the translation, including pods and networks, and the docker run to quadlet converter handles single commands.
Frequently asked questions
Which podman version do I need for quadlets?
Quadlets arrived in podman 4.4. Leap 16 (podman 5.x) and Tumbleweed (newest podman) both have them. The podman quadlet subcommand (install, list, rm) is newer, from podman 5.6, so it is on Tumbleweed but not on Leap 16.0.
Can I use systemctl enable on a quadlet service?
No. The service is generated, and systemctl refuses to enable generated units. Put WantedBy=default.target (rootless) or WantedBy=multi-user.target (rootful) in the [Install] section of the .container file instead, and run daemon-reload.
Do I still need podman generate systemd?
No. podman generate systemd is deprecated; quadlets replace it. Existing generated unit files keep working, but new services should be quadlets.