🇺🇸 JULY 4TH SALE! 20% OFF on All Plans - Limited Time! Use JULY4TH20 Claim Offer →

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 run or podman start commands inside ExecStart and ExecStop directives
  • 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 ExecStartPre line 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 ExecStart line. 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 ExecStart line 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 packed ExecStart line
  • PublishPort reads clearly instead of being buried in -p 8080:80 inside a string
  • AutoUpdate=registry hooks directly into podman 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/
  • How the unit is generated
    • Hand-written: you write the full .service file yourself, or generate it once with podman generate systemd and then maintain it manually
    • Quadlet: systemd's generator rebuilds the .service file from your .container file automatically on every daemon-reload
  • Updating configuration
    • Hand-written: edit the ExecStart line directly, easy to introduce a syntax mistake
    • Quadlet: edit a single declarative field, regenerate, the structure stays valid
  • Image auto-updates
    • Hand-written: requires a separate timer or script calling podman auto-update
    • Quadlet: built in with AutoUpdate=registry
  • Multi-container dependencies
    • Hand-written: manual After= and Requires= lines pointing at other hand-written services
    • Quadlet: same directives, but referencing other Quadlet units directly by name with automatic translation
  • Underlying mechanism
    • Both ultimately produce a standard systemd .service unit
    • Both are managed with the exact same systemctl commands once running

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 ExecStart invocation
    • 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-reload after editing a Quadlet file. Quadlet files are not unit files. systemd needs to regenerate the actual .service file 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 --user on every command for rootless services. Mixing root and user-level systemctl commands 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 .container files as something you manage with systemctl edit. You edit the Quadlet file directly, then reload. The generated .service file 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 systemctl commands 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