Skip to content

Quadlet

Tool · prereq 03 (Containers/Podman), 09 (RHEL) · ~half a day–1 day

A container you launched by hand in module 03 (Containers) doesn't survive a reboot and isn't managed like every other service on the box. Quadlet closes that gap: you write a declarative .container/.volume/.network file, and systemd — the service manager from module 09, Linux (RHEL) — generates and owns the real unit from it, so the container starts at boot, restarts on failure, and is inspected with the same systemctl/journalctl tools as any other service. This module is about that generate-from-source loop, and the one habit that trips people up: editing the source file alone does nothing until you tell systemd to regenerate.

TL;DR — in 30 seconds:

  • Editing a .container/.volume/.network file does nothing on its own — you must systemctl daemon-reload before the generator re-reads it and rebuilds the real unit.
  • Quadlet replaces podman generate systemd: you edit a declarative source file and reload, instead of regenerating a static shell-script-like unit by hand every time the container config changes.
  • Never hand-edit the generated .service file — it's rebuilt from the source Quadlet file on every reload, so direct edits to it are effectively thrown away.

Learning objectives

By the end, the junior can:

  • Write a .container Quadlet unit and run a container as a systemd service.
  • Explain why daemon-reload is needed and how Quadlet replaces podman generate systemd.
  • Attach a .volume / .network to a Quadlet.

Do it with Claude

Pair, Do and Prove run in Claude Code, with the onboarding-tutor skill. Click a button to copy the exact prompt, then paste it into Claude Code in this repo.

1. Learn · acquire the concept

Read: the quadlet coursefocus §4 (.container), §5 (.volume), §6 (.network), §12 (operations — the daily loop). Skip for now: §8 (.kube), §9 (.build/.image) — later. Glossary (beat 5): the module’s Anki deck

Check yourself — you edit a key in myapp.container and run systemctl restart myapp.service, but the container still runs with the old configuration. What step did you skip, and why?

systemctl daemon-reload — a plain restart just reruns the existing generated unit. Quadlet's generator only re-reads the source .container file and rebuilds the service on daemon-reload (or at boot), so a restart without a reload first just restarts the stale, unchanged unit.

Check yourself — why is Quadlet preferred over the older podman generate systemd approach?

Quadlet units are declarative source files that get regenerated automatically on daemon-reload — you edit the .container/.volume/.network file and reload. podman generate systemd instead produced a static, one-off unit file that had to be manually regenerated and rewritten by hand every time the container's configuration changed.

2. Pair · passive → active

Drive the onboarding-tutor skill:

  • "Explain what Quadlet generates and where the real systemd unit ends up."
  • "3-line mental model of the .containersystemctl daemon-reloadsystemctl start loop."
  • "Quiz me: why does my edit not take effect until I do X?"

Common gotchas

  • Hand-editing the generated .service file instead of the source Quadlet file. Fix: always edit the source .container/.volume/.network file and daemon-reload — the generated unit is rebuilt from source on every reload, so direct edits to it don't survive.
  • Forgetting daemon-reload after adding or editing a Quadlet file. Fix: systemd only reruns the Quadlet generator on daemon-reload (or boot) — without it, the edit is silently not picked up at all, even though nothing looks obviously wrong.
  • Dropping a rootless quadlet file in the root path (/etc/containers/systemd/), or vice versa. Fix: rootless quadlets live under ~/.config/containers/systemd/; root quadlets live under /etc/containers/systemd/ — the wrong directory means the wrong systemd instance never sees the file.
  • Assuming systemctl --user enable --now alone survives a logout. Fix: a rootless service also needs loginctl enable-linger <user> — without it, the user systemd instance (and everything in it) stops the moment the last session ends.

Done when: you can explain the daemon-reload step before you forget it, unprompted.

3. Do · produce an artifact

Exercise — promote your module 03 (Containers) container to a systemd service:

  1. Use a systemd Linux host (course §3) — Quadlet is systemd-native: a RHEL/Linux VM (module 09, Linux (RHEL)), not macOS. Needs Podman + systemd.
  2. Take the image you built in module 03 (Containers) and write a .container Quadlet unit for it, with a .volume for its state.
  3. Drop the files in the right dir, systemctl daemon-reload, then systemctl --user enable --now (rootless) the generated service.
  4. Verify with systemctl status + journalctl. Reboot or restart the service and confirm state persists (the volume).

Done when: the container runs as a managed systemd service and survives a restart with its data.

4. Prove · understanding gate

Mandatory. Pass bar in the Socratic gate.

  1. Walkthrough: Explain each key in your .container file and which systemd unit it generated.
  2. Alternative: Quadlet vs the old podman generate systemd — why is Quadlet preferred now?
  3. Failure & debug: You edited the .container file but nothing changed — why? And when systemd says the unit doesn't exist, what are the two usual causes?
  4. Provenance: Which keys did the agent add? How did you verify the generated unit matches your intent (systemctl cat)?

Pass bar: you can defend the Quadlet→systemd generation and the daily loop. Forgetting why daemon-reload matters = back to Pair.

5. Retain · fight forgetting

Study the module deck SE Onboarding::10 Quadlet — its 11 theme subdecks let you drill one area at a time. It syncs to your Anki automatically on pull (anki-sync).

Done when: you can recall — from the deck — .container/.volume/.network, daemon-reload, and the rootless quadlet dirs.

Key takeaways

  • Quadlet is declarative: you edit a source .container/.volume/.network file and the generator rebuilds the real systemd unit from it — you never author or hand-maintain the unit itself.
  • systemctl daemon-reload is the step that makes an edit take effect — the generator only re-reads Quadlet source files on reload (or at boot); skip it and the change is silently not picked up.
  • Never hand-edit the generated .service file — it's rebuilt from the source Quadlet file on every reload, so direct edits to it don't survive.
  • Rootless quadlets live under ~/.config/containers/systemd/; root quadlets live under /etc/containers/systemd/ — the wrong directory means the wrong systemd instance never sees the file.
  • A rootless service also needs loginctl enable-linger <user> to survive logout — systemctl --user enable --now alone stops the service the moment the last session ends.
  • Quadlet replaces podman generate systemd: instead of manually regenerating a static unit file by hand every time the container config changes, you edit a declarative source file and reload.

Go deeper — curated resources

Hand-picked external material to take this topic further — the best books, courses, talks, and writing. Cost is tagged Free, Free online (full text/course free on the web), or Paid.

  • Docs · Quadlet — podman-systemd.unit(5) — Podman project. The reference for Quadlet unit files: every key for .container/.volume/.network and how it maps to podman. Free.
  • Blog · Make systemd better for Podman with Quadlet — Dan Walsh (Red Hat). A practical, hands-on intro to running containers under systemd with Quadlet, from Podman's lead. Free.
  • Docs · systemd.service man page — freedesktop.org. What a unit file can actually express underneath — service types, ExecStart, Restart=, dependencies. Free.

Gate log (mentor fills on pass)

  • Date passed: / Passed by: / checked by: / Weak spots: