Chapter 21: Containers with Podman

Containers as services with Quadlet

A container you started by hand is gone after a reboot. To run it as a real service, describe it in a Quadlet file. Podman turns that file into a systemd service, so everything you learned in chapter 9 (start, status, restart, journal) applies to your container too.

From a file to a service

From a file to a service: Step through Quadlet.

A Quadlet file: Select a section or setting.

Put the file in the directory below. Start from the page and image of the earlier lessons (the page is in ~/web):

[student@servera ~]$ mkdir -p ~/.config/containers/systemd
[student@servera ~]$ cat > ~/.config/containers/systemd/web.container <<'EOT'
[Unit]
Description=Demo web container

[Container]
Image=registry.access.redhat.com/ubi9/httpd-24:latest
PublishPort=8080:8080
Volume=%h/web:/var/www/html:ro,Z

[Service]
Restart=always

[Install]
WantedBy=default.target
EOT
[student@servera ~]$ systemctl --user daemon-reload
[student@servera ~]$ systemctl --user start web.service
[student@servera ~]$ systemctl --user is-active web.service
active
[student@servera ~]$ curl -s localhost:8080
hello from a container
[student@servera ~]$ podman ps --format "{{.Names}} {{.Status}}"
systemd-web Up 3 seconds

The container is named systemd-web (the prefix systemd- plus the file name). systemctl --user cat web.service shows the generated unit, which begins with the comment "Automatically generated by …podman-user-generator". Edit the .container file, never the generated one, and run daemon-reload after every change.

Restart behaviour

[student@servera ~]$ systemctl --user is-enabled web.service
generated
[student@servera ~]$ podman kill systemd-web
systemd-web
[student@servera ~]$ systemctl --user is-active web.service
active
[student@servera ~]$ systemctl --user show web.service -p NRestarts
NRestarts=1

You do not run enable: a generated unit shows generated, and the [Install] section of your file does the work. Restart=always restarted the killed container; systemctl --user status web.service shows the application's own log lines as well.

Surviving a reboot

By default a user's services run only while that user is logged in. Lingering keeps the user's systemd alive from boot:

[student@servera ~]$ loginctl show-user student -p Linger
Linger=no
[student@servera ~]$ sudo loginctl enable-linger student
[student@servera ~]$ loginctl show-user student -p Linger
Linger=yes
[student@servera ~]$ sudo firewall-cmd --permanent --add-port=8080/tcp; sudo firewall-cmd --reload
success
success

With lingering on, a reboot is followed by a running container, without anyone logging in; a client on serverb gets the page. Without it, the same client gets "Connection refused" until student logs in for the first time. Lingering is a deliberate policy: it lets a user run programs without a session, so enable it only for service accounts that need it.

When it does not work

SymptomCheck
Failed to connect to busYou are not in a real login; ssh in as the user
Service fails at oncesystemctl --user status web.service, journalctl _SYSTEMD_USER_UNIT=web.service and podman logs systemd-web
Start times outPull the image first; a slow registry is the usual cause
Page gives 403The volume needs :Z
Address already in useAnother container or program uses the host port (ss -tlnp)
Works locally onlyAllow the port in the firewall
Does not start after rebootLingering is off

A wrong image name shows up in the journal as Error: unable to copy from source docker://…/httpd-99:latest, and systemctl --user is-active keeps saying activating while Restart=always retries.

The earlier podman generate systemd command is deprecated in favour of Quadlet. Use .container files for new work.

Try it

Check your understanding