Quadlet vs Systemd: Running Podman Containers the Right Way
Published On: 26 June 2026
Objective
This guide compares Quadlet against hand-written systemd unit files for managing Podman containers. By the end, you will know:
- What Quadlet actually does and how it differs from writing systemd units by hand
- The exact syntax for both approaches, side by side
- When to use Quadlet and when a manually written unit file still makes sense
- What this means for RHCSA preparation in 2026
If you've managed containers on RHEL for any length of time, you've run into the question of how to make a container survive a reboot. Podman alone doesn't do that. systemd does. The question isn't whether to involve systemd, it's how you get there.
The Core Problem: Containers Are Not Services by Default
A container started with podman run exists only as long as that process or your terminal session does. It doesn't restart on failure. It doesn't start at boot. It doesn't integrate with systemctl status, journald, or boot ordering.
For a container to behave like any other managed service on a Linux system, it needs a systemd unit file. There are two ways to get one:
- Write the unit file by hand, wrapping
podman runorpodman startcommands insideExecStartandExecStopdirectives - Use Quadlet, which lets you describe the container declaratively and generates the unit file for you
Both get you to the same place: a container managed by systemd. How you get there is where the real difference lives.
Method 1: Hand-Written Systemd Unit Files
This is the traditional approach. You write a .service file directly, and the ExecStart line calls Podman to run the container.
cat > /etc/systemd/system/nginx-container.service << EOF
[Unit]
Description=Nginx Web Server Container
After=network-online.target
Wants=network-online.target
[Service]
Restart=always
ExecStartPre=/usr/bin/podman rm -f nginx-web
ExecStart=/usr/bin/podman run --name nginx-web -p 8080:80 docker.io/library/nginx:latest
ExecStop=/usr/bin/podman stop -t 10 nginx-web
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now nginx-container.service
systemctl status nginx-container.service
This works. It's also the method most RHCSA materials still teach, and it maps closely to the older podman generate systemd workflow, where you'd run a container manually, generate a unit file from it, then copy that file into place.
The friction shows up over time:
- You're responsible for cleanup logic. The
ExecStartPreline removing any existing container by that name is a manual safeguard you have to remember to add. - Port mappings, volumes, and environment variables all live inside one long
ExecStartline. As the container's configuration grows, that line becomes hard to read and easy to break. - There's no validation. A typo in a flag fails silently until the service tries to start.
- Updating the image means editing the
ExecStartline directly, not a clean declarative field.
Method 2: Quadlet
Quadlet is Podman's native systemd integration. Instead of writing a .service file, you write a .container file using a simple INI-style format that looks like a hybrid of a systemd unit and a podman run command. A systemd generator reads that file and builds the full service unit automatically, every time systemd starts or reloads.
mkdir -p ~/.config/containers/systemd/
cat > ~/.config/containers/systemd/nginx.container << EOF
[Unit]
Description=Nginx Web Server Container
After=network-online.target
[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now nginx.service
systemctl --user status nginx.service
Notice the file is named nginx.container, but you manage it with systemctl as nginx.service. Quadlet handles that translation. A few things worth pointing out:
- There's a dedicated
[Container]section instead of one packedExecStartline PublishPortreads clearly instead of being buried in-p 8080:80inside a stringAutoUpdate=registryhooks directly intopodman auto-update, something you'd otherwise script separately- No manual cleanup step is needed. Quadlet manages container naming and replacement for you.
For system-wide (root) services instead of per-user services, the file goes in /etc/containers/systemd/ instead of ~/.config/containers/systemd/, and you drop --user from the systemctl commands.
Quadlet for Volumes and Networks
Quadlet isn't limited to containers. It extends the same declarative approach to volumes and networks using separate file types, and these can reference each other by name.
# ~/.config/containers/systemd/web-data.volume
[Volume]
# ~/.config/containers/systemd/app.network
[Network]
Subnet=192.168.30.0/24
Gateway=192.168.30.1
# ~/.config/containers/systemd/nginx.container
[Container]
Image=docker.io/library/nginx:latest
Volume=web-data.volume:/usr/share/nginx/html
Network=app.network
PublishPort=8080:80
Each file becomes its own systemd unit. systemd handles the dependency ordering between them automatically, so the volume and network are created before the container that depends on them tries to start.
Quadlet for Multi-Container Applications
If you've used docker-compose or podman-compose to define multi-container stacks, Quadlet replaces that with native systemd dependencies instead of a separate orchestration tool.
# ~/.config/containers/systemd/db.container
[Unit]
Description=Database Container
[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
# ~/.config/containers/systemd/api.container
[Unit]
Description=API Server Container
Requires=db.service
After=db.service
[Container]
Image=myregistry/api:latest
Environment=DB_HOST=db
The Requires= and After= directives in the API container's file ensure the database service starts first. Quadlet translates a dependency on another Quadlet unit into a dependency on the systemd service it generates, so the ordering works exactly the way it would between any two native systemd services.
Side-by-Side Comparison
- Syntax style
- Hand-written: standard systemd unit syntax, container logic embedded in shell commands
- Quadlet: dedicated
[Container],[Volume], and[Network]sections with Podman-aware fields
- File location
- Hand-written:
/etc/systemd/system/or~/.config/systemd/user/ - Quadlet:
/etc/containers/systemd/or~/.config/containers/systemd/
- Hand-written:
- How the unit is generated
- Hand-written: you write the full
.servicefile yourself, or generate it once withpodman generate systemdand then maintain it manually - Quadlet: systemd's generator rebuilds the
.servicefile from your.containerfile automatically on every daemon-reload
- Hand-written: you write the full
- Updating configuration
- Hand-written: edit the
ExecStartline directly, easy to introduce a syntax mistake - Quadlet: edit a single declarative field, regenerate, the structure stays valid
- Hand-written: edit the
- Image auto-updates
- Hand-written: requires a separate timer or script calling
podman auto-update - Quadlet: built in with
AutoUpdate=registry
- Hand-written: requires a separate timer or script calling
- Multi-container dependencies
- Hand-written: manual
After=andRequires=lines pointing at other hand-written services - Quadlet: same directives, but referencing other Quadlet units directly by name with automatic translation
- Hand-written: manual
- Underlying mechanism
- Both ultimately produce a standard systemd
.serviceunit - Both are managed with the exact same
systemctlcommands once running
- Both ultimately produce a standard systemd
When to Use Which
- Use Quadlet when:
- You're setting up new container services on RHEL 9 or later
- You're managing more than one or two containers and want clean dependency handling between them
- You want built-in auto-update support without writing your own timer
- You want configuration that's easy to read and modify without touching shell syntax
- You're working in a team where others will need to read and maintain these files later
- A hand-written unit file still makes sense when:
- You're on an older RHEL version where Quadlet isn't available or well-supported
- You need extremely fine-grained control over the exact
ExecStartinvocation - You're working with existing automation or configuration management that already expects standard systemd unit files and you don't want to introduce a new file type
- The container setup is genuinely a one-off with no real maintenance expected
For nearly all new work on current RHEL systems, Quadlet is the recommended direction. It is not a competing tool fighting systemd for control, it's a thin, declarative layer that produces the same systemd services you'd get by hand, with less room for mistakes.
Verifying a Quadlet Service Like Any Other
This is the part that surprises people coming from Docker Compose: once a Quadlet container is running, there is nothing container-specific about how you manage it. It behaves like any other systemd unit.
# Check status
systemctl --user status nginx.service
# View logs
journalctl --user -u nginx.service
# Restart it
systemctl --user restart nginx.service
# Confirm the container itself is running
podman ps
# Confirm it starts at boot (enable lingering for user services)
loginctl enable-linger $(whoami)
That last command matters if you're running rootless Quadlet services under your own user account. Without lingering enabled, user-level systemd services stop when you log out. Enabling lingering keeps them running independent of an active login session.
Quadlet and the RHCSA Exam
This is worth being precise about, because a lot of outdated material conflates "containers are on the exam" with "Quadlet specifically is on the exam."
- The official RHCSA exam objectives list container management with Podman as a tested category, including running, starting, stopping, and listing containers, and configuring a container to start automatically as a systemd service
- Older official guidance and most current third-party prep material describe this primarily through
podman generate systemd, the hand-written unit file workflow covered earlier in this guide - Quadlet is the direction Red Hat and the upstream Podman project are clearly moving, and it is increasingly documented as the recommended approach for running containers under systemd on RHEL
The honest takeaway: know both. Understand how to generate a systemd unit from a running container the traditional way, since that's what most current exam objectives and study material directly describe. But also understand Quadlet, since it's how you'll actually do this work in production, and exam content tends to follow real-world practice over time. If you only learn one, you're taking a bet on exam wording that isn't necessary when both methods are this closely related.
Either way, the same RHCSA fundamentals apply: the service must be enabled, not just started, and the entire configuration must survive a reboot. That part doesn't change no matter which method you use.
Practice tip: Set up the same container both ways, once with a hand-written unit file and once with Quadlet. Reboot the system after each. If both survive and both show up correctly in
systemctl status, you understand the relationship between the two well enough for the exam and for real work.
Common Mistakes
- Forgetting
daemon-reloadafter editing a Quadlet file. Quadlet files are not unit files. systemd needs to regenerate the actual.servicefile from them before changes take effect. - Mixing up file locations. A root-level Quadlet file placed in the user directory, or vice versa, won't be picked up by the generator you expect.
- Forgetting
--useron every command for rootless services. Mixing root and user-levelsystemctlcommands against the same container leads to confusing "unit not found" errors. - Not enabling lingering for rootless services that need to survive logout. The service looks correctly enabled but stops the moment the session ends.
- Treating
.containerfiles as something you manage withsystemctl edit. You edit the Quadlet file directly, then reload. The generated.servicefile is not meant to be hand-edited.
How LinuxCert.Guru Helps You Practice This
Containers and systemd integration are exactly the kind of topic that's hard to learn from reading alone. The commands are simple, but the failure modes (a missing reload, a path mistake, a forgotten lingering setting) only become obvious when you've hit them yourself. LinuxCert.Guru's browser-based labs include dedicated Podman and systemd modules covering both the traditional unit-file workflow and Quadlet, with auto-graded tasks that check whether your service is actually enabled and actually survives a reboot, not just whether it started once.
Conclusion
Quadlet and hand-written systemd unit files solve the same problem: making a Podman container behave like a properly managed service. The difference is in how much of the boilerplate you write yourself versus how much gets generated for you from a cleaner, declarative file.
- Hand-written units give you full manual control and match what most current RHCSA study material teaches
- Quadlet gives you cleaner syntax, built-in auto-update support, and easier multi-container dependency management, and reflects where Podman and RHEL are heading
- Both produce a standard systemd service, managed with the same
systemctlcommands once running - For the exam, know both. For new production work, default to Quadlet.
The underlying skill that matters either way is the same one tested throughout the RHCSA: can you make a service start correctly, enable it properly, and prove it survives a reboot. Master that, and switching between the two methods is a syntax difference, not a conceptual one.
Start practicing Podman, systemd, and Quadlet on real hands-on labs at LinuxCert.Guru → https://linuxcert.guru