Managing the container lifecycle as advanced systemd services: socket activation for on-demand services via socket units, image auto-update with podman auto-update, Quadlet unit generation, and safe update and rollback strategies.

In episode 16 you hardened containers with resource limits, read-only rootfs, and auditing. Now comes the peak of the systemd phase: how to make containers behave like production services — starting only when needed, updating themselves when a new image is available, and rolling back to an old version in seconds. Episode 17 brings together Quadlet, socket activation, and podman auto-update.
In episode 10 you learned about Quadlet: .container, .pod, .volume, and .image systemd units that are automatically translated into regular systemd units. Its advantage is now clear: these units can leverage all of systemd's features, including dependencies, restart policies, and the socket activation you're about to see.
Socket activation lets a service only start when a connection arrives. systemd holds the socket first; when a request comes in, systemd launches the container. The result: the container doesn't consume resources while idle.
The manual way, with a socket unit:
[Unit]
Description=Socket untuk app container
[Socket]
ListenStream=8080
SocketMode=0660
[Install]
WantedBy=sockets.targetAnd on the Quadlet side, the .container unit is paired with a .socket file with the same name. Quadlet recognizes this pair and connects them automatically:
[Unit]
Description=App container on-demand
[Container]
Image=quay.io/example/app:1.2
PublishPort=127.0.0.1:8080:8080
[Service]
Restart=on-failure
[Install]
WantedBy=default.targetEnable the pair:
systemctl --user daemon-reload
systemctl --user enable --now app.socket
systemctl --user status app.socketAs long as there's no connection to port 8080, the container doesn't run — saving memory and CPU. As soon as a request arrives, systemd starts the container within seconds.
One of the biggest operational challenges: ensuring containers use the latest image without manual work. podman auto-update solves it. Containers you want updated are marked with a label:
[Container]
Image=docker.io/library/nginx:1.26
Label=io.containers.autoupdate=registryThen run the update:
podman auto-update --dry-run
podman auto-updatepodman auto-update --dry-run shows which containers have a new image version without changing anything; without --dry-run it pulls the new image and restarts the container. For locally built containers, use io.containers.autoupdate=local.
Important
Auto-update replaces the image a unit references, but the restart policy stays with that unit. Make sure Restart=always or on-failure is set in the Quadlet [Service] section, and test --dry-run on staging first before letting it run in production.
Writing Quadlet from scratch can be tedious for already-running containers. Podman provides a generator to reverse the process: from an active container or pod to a Quadlet unit file:
podman systemd generate --name myapp --files
ls -la ~/.config/containers/systemd/podman systemd generate --files writes .container or .pod files ready to copy into the Quadlet directory. For a classic systemd unit style, podman generate systemd still exists — but Quadlet is the recommended direction going forward.
A safe update strategy isn't just pulling the latest image, it's making sure you can go back:
latest in production; use version tags like 1.2.3.systemctl --user restart.| Phase | Tool | Command / action |
|---|---|---|
| Definition | Quadlet .container | Write the file in the systemd directory |
| Activation | systemd | systemctl --user enable --now |
| On-demand | Socket activation | systemctl --user start app.socket |
| Update | podman auto-update | podman auto-update |
| Rollback | Image tag | Change the tag, restart the unit |
| Inspection | systemd + Podman | systemctl --user status, podman logs |
The combination of these tools turns a container from an object run once into a service fully managed by systemd.
In episode 17 you managed the advanced service lifecycle: socket activation with systemd units and Quadlet pairs, automatic updates with podman auto-update, Quadlet unit generation via podman systemd generate, and safe tag-based update and rollback strategies.
The key points to take home:
--dry-run before trusting automation.podman systemd generate.In the next episode, Episode 18, we shift from choreography to performance: performance & optimization — how layer caching and multi-stage builds speed up the pipeline, leveraging zstd:chunked partial pulls, and maintaining storage with garbage collection and pruning.