whoami quadlet

The hello world of quadlets: one tiny web server as a systemd user service.

  • Standalone container
  • Rootless: systemctl --user
  • Tested with podman 5.0 and newer

Last updated

In short: Save the 14-line whoami.container below in ~/.config/containers/systemd/, run systemctl --user daemon-reload, then systemctl --user start whoami.service. curl http://localhost:8080 answers with the container's hostname, its IP addresses and your request headers. Use it to prove that podman, quadlet and systemd work together before you deploy anything real.

The quadlet files

Every quadlet file below passes podman's quadlet generator and systemd-analyze verify on podman 5.0 and the current release. Personal values are placeholders: replace example.com, /srv/... paths and the time zone with your own.

whoami.container

Save as ~/.config/containers/systemd/whoami.container

[Unit]
Description=whoami test web server

[Container]
Image=docker.io/traefik/whoami:v1.12
ContainerName=whoami
AutoUpdate=registry
PublishPort=8080:80

[Service]
Restart=always

[Install]
WantedBy=default.target

Why is it built this way?

whoami is the smallest useful quadlet: one .container file and nothing else. It has no .volume because the server keeps no data, and no .network because the default network is enough for one container that only needs a published port.

Rootless. The file lives in ~/.config/containers/systemd/, so it runs under your own user with systemctl --user. Nothing here needs root: port 8080 is above 1024, and inside the container the server may still listen on port 80.

Pinned tag with auto-updates. traefik/whoami:v1.12 is a tag that follows the 1.12 patch releases. AutoUpdate=registry lets podman auto-update pull those patches and restart the service, and podman rolls back if the new image fails to start. Auto-updates only work with a fully qualified image name such as docker.io/traefik/whoami.

Restart and boot. Restart=always restarts the container when it exits, and WantedBy=default.target starts it with your user session, or at boot once lingering is on. Quadlet also adds a wait for the network to be online, so you do not need an After=network-online.target line. Every key is documented in podman-systemd.unit(5).

A request to curl http://localhost:8080 answers like this (addresses replaced by examples):

Hostname: 3e37468f5a20
IP: 127.0.0.1
IP: ::1
RemoteAddr: 10.0.2.100:43038
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/8.21.0
Accept: */*

How do I start it?

Run these as your normal user, not as root. Quadlet reads ~/.config/containers/systemd/ for user services.

mkdir -p ~/.config/containers/systemd
# save whoami.container there, then:
systemctl --user daemon-reload
systemctl --user start whoami.service
curl http://localhost:8080
# keep user services running after you log out:
loginctl enable-linger "$USER"

Check the result with systemctl --user status and the logs with journalctl --user -u followed by the service name. To turn a docker-compose file of your own into files like these, use the compose to quadlet converter.

Common mistakes

Failed to enable unit: Unit whoami.service is transient or generated

Quadlet units cannot be enabled with systemctl enable. The [Install] section of the .container file does that job: WantedBy=default.target makes the generator link the service into default.target on every daemon-reload, so it starts at boot.

Unit whoami.service not found after daemon-reload

The generator skipped the file: a typo in a key, a wrong section name, or the file is not in ~/.config/containers/systemd/. Run /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun (or /usr/libexec/podman/quadlet --user --dryrun on some distributions) to see the error.

The container stops when I log out of SSH

User services only run while you have a session, unless lingering is on. Run loginctl enable-linger once for your user; the service then also starts at boot without a login.

Frequently asked questions

What is the smallest working podman quadlet?

A .container file with an Image= line in a [Container] section is enough for the generator. In practice add ContainerName=, PublishPort= for access, Restart=always under [Service] and WantedBy=default.target under [Install], as in the whoami.container on this page.

Why use traefik/whoami to test a podman setup?

The image is about 13 MB, needs no configuration or volume, and answers every HTTP request with its hostname, IP addresses and the request headers. That makes it ideal for checking a quadlet, a published port or the headers a reverse proxy passes on.

How do I see the logs of a quadlet container?

Quadlet containers log to the systemd journal. Run journalctl --user -u whoami.service for a rootless container, or add -f to follow new lines. podman logs whoami shows the same output.

How do I remove a quadlet service again?

Stop it with systemctl --user stop whoami.service, delete the .container file, and run systemctl --user daemon-reload. The generated unit disappears; remove the image with podman rmi if you no longer need it.