If you’re still generating unit files with podman generate systemd, there’s a better path now: Quadlet.
In current Podman docs, podman generate systemd is marked deprecated (still available, but no new features), and Quadlet is the recommended approach.
This guide gives you a practical, reproducible setup for:
- a rootless container managed by systemd
- declarative
.containerfiles (instead of generated unit files) - safe image auto-updates with rollback support
- basic observability and troubleshooting
Why switch to Quadlet?
podman generate systemd creates unit files from existing containers. That works, but it’s imperative and easy to drift.
Quadlet flips this into a declarative model:
- you define desired state in
.container,.network,.volume, etc. - systemd (via Podman’s generator) creates/updates corresponding
.serviceunits ondaemon-reload - config is versionable and easier to review
Also, the Podman manual explicitly recommends Quadlet over podman generate systemd for systemd-managed workloads.
Prerequisites
- Linux host with systemd and Podman installed
- user-level systemd session available (
systemctl --user ...) - outbound registry access (for pulling images)
Check Podman and cgroup mode:
podman --version
podman info --format '{{.Host.CgroupsVersion}}'
Quadlet requires cgroup v2.
Step 1) Create a rootless Quadlet file
For rootless units, place files under:
-
~/.config/containers/systemd/(recommended)
Create directories:
mkdir -p ~/.config/containers/systemd
mkdir -p ~/.config/containers/systemd/data/whoami
Now create ~/.config/containers/systemd/whoami.container:
[Unit]
Description=Traefik whoami (rootless Podman via Quadlet)
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/traefik/whoami:v1.10
ContainerName=whoami
PublishPort=127.0.0.1:18080:80
# Optional persistent data example:
Volume=%h/.config/containers/systemd/data/whoami:/data:Z
# Enable automatic image updates via registry digest checks
AutoUpdate=registry
[Service]
Restart=always
RestartSec=5
# Give image pulls/builds enough time during startup
TimeoutStartSec=900
[Install]
WantedBy=default.target
Why bind to 127.0.0.1?
Publishing on loopback (127.0.0.1) keeps the app private to the host unless you intentionally front it with a reverse proxy.
Step 2) Reload systemd user daemon and start service
systemctl --user daemon-reload
systemctl --user start whoami.service
systemctl --user enable whoami.service
Check status and logs:
systemctl --user status whoami.service --no-pager
journalctl --user -u whoami.service -n 100 --no-pager
podman ps --filter name=whoami
curl -s http://127.0.0.1:18080
Step 3) Enable periodic auto-updates
Podman ships podman-auto-update.service and podman-auto-update.timer.
By default, the timer runs daily at midnight.
Enable for your user:
systemctl --user enable --now podman-auto-update.timer
systemctl --user list-timers | grep podman-auto-update
Run a dry-run check:
podman auto-update --dry-run
If an image digest changes and your container has AutoUpdate=registry, Podman pulls the new image and restarts the related systemd unit.
Step 4) Optional: expose through Caddy
If you want HTTPS and friendly hostnames, proxy your loopback service.
Minimal Caddyfile:
whoami.example.com {
reverse_proxy 127.0.0.1:18080
}
Reload Caddy and test.
Operational notes that save headaches
-
Use fully-qualified image names with
AutoUpdate=registry(e.g.,docker.io/...,quay.io/...). -
Raise
TimeoutStartSecfor images that may pull slowly. -
Use drop-ins (
*.container.d/*.conf) for environment-specific overrides instead of editing the base file.
Troubleshooting quick list
If systemd says unit not found after creating .container:
systemctl --user daemon-reload
systemctl --user list-unit-files | grep whoami
Inspect generated units and generator behavior:
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
systemd-analyze --user --generators=true verify whoami.service
If auto-updates do not trigger:
podman auto-update --dry-run
journalctl --user -u podman-auto-update.service -n 200 --no-pager
podman inspect whoami --format '{{ index .Config.Labels "io.containers.autoupdate" }}'
Final take
If you want systemd-managed containers on Linux without bringing in full orchestration, Quadlet is the cleanest day-2 operations model right now.
You keep:
- rootless security posture
- declarative, reviewable config
- native systemd lifecycle + logs
- built-in update workflow with rollback support
That’s a solid production baseline for single-host services.
References
- Podman Quadlet and systemd unit integration (
podman-systemd.unit): https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html - Podman Quadlet CLI (
podman-quadlet): https://docs.podman.io/en/latest/markdown/podman-quadlet.1.html -
podman generate systemddeprecation notice: https://docs.podman.io/en/latest/markdown/podman-generate-systemd.1.html - Podman auto-update behavior + systemd timer: https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html
- Caddy reverse proxy directive docs: https://caddyserver.com/docs/caddyfile/directives/reverse_proxy
Top comments (0)