| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64 |
| Applies to | Any Linux host with Podman and systemd |
| Privilege | sudo for rootful examples on the lab host |
| Scope | Quadlet concept, generator model, search paths, first .container service, service vs container naming, [Install] boot wiring, daemon-reload, start/status/logs, service types overview, and restart behavior. Does not cover full [Container] directive reference, .pod tutorials, rootless linger depth, auto-update, or full troubleshooting workflows. |
| Related guides | Podman pods Podman port mapping |
Quadlet turns Podman container definitions into normal systemd services. You maintain a small declarative file such as quadlet-demo.container, reload systemd, and manage quadlet-demo.service with systemctl while Podman creates the container at start time.
This guide walks through the generator model, your first rootful .container unit, and the naming and enablement rules that trip up experienced systemd administrators.
quadlet-demo.container), not the generated unit under the systemd generator output. After every change, run daemon-reload and restart the generated service.
What is Podman Quadlet?
Quadlet lets you describe Podman resources with systemd-style declarative files:
web.container
data.volume
app.network
application.pod
image.build
base.image
config.artifact
workload.kubeAt boot or after systemctl daemon-reload, the Podman Quadlet generator reads these files and writes ordinary systemd service units.
quadlet-demo.container
│
▼
Podman Quadlet generator
│
▼
quadlet-demo.service
│
▼
podman run ...
│
▼
Podman container (quadlet-demo)Keep three names separate:
Quadlet source file: quadlet-demo.container
Generated systemd unit: quadlet-demo.service
Podman container: quadlet-demo (when ContainerName= is set)The generated unit is quadlet-demo.service. The Podman container name comes from ContainerName= or defaults to systemd-<unit-basename> — it is not the service name itself.
Why use Quadlet instead of a handwritten podman run unit?
A hand-written systemd unit pushes lifecycle details onto you:
[Service]
ExecStart=/usr/bin/podman run ...
ExecStop=/usr/bin/podman stop ...
ExecStopPost=/usr/bin/podman rm ...You must handle container naming, conmon and PID behavior, dependencies, network and volume ordering, sdnotify integration, and Podman-specific stop and cleanup paths yourself.
Quadlet translates declarative container configuration into the correct generated service:
[Container]
Image=registry.access.redhat.com/ubi9/ubi-minimal
ContainerName=quadlet-demo
Exec=sleep 3600Hand-written ExecStart=podman run units are still possible. Quadlet is the supported Podman-native declarative approach for new systemd deployments.
Quadlet vs podman generate systemd
podman generate systemd followed an old workflow:
podman run/create
↓
existing container
↓
podman generate systemd
↓
generated .serviceQuadlet inverts that model:
desired .container configuration
↓
systemd generator
↓
.service
↓
container created from declarationpodman generate systemd is deprecated for new work. The desired configuration file is the source of truth, not a container you created earlier on the CLI. Converting existing generated units is covered in Migrate from podman generate systemd to Quadlet.
Check Quadlet requirements
Confirm Podman and cgroup layout before you create the first unit:
podman --versionSample output:
podman version 5.8.2Quadlet requires cgroup v2:
podman info --format '{{.Host.CgroupsVersion}}'Sample output:
v2Check systemd and locate the generator on your distribution:
systemctl --versionSample output:
systemd 257 (257-23.el10_2.2-g009f2a8)Generator paths vary by package layout. On RHEL 10 with Podman 5.8.2:
rpm -ql podman | grep -E 'quadlet|system-generator'Sample output (trimmed):
/usr/lib/systemd/system-generators/podman-system-generator
/usr/libexec/podman/quadletUse the podman-system-generator binary your package provides. Do not assume one universal path across distributions.
Quadlet unit types
| Extension | Purpose |
|---|---|
.container |
Run a container |
.pod |
Create and manage a Podman pod |
.volume |
Ensure a Podman volume exists |
.network |
Ensure a Podman network exists |
.build |
Build an image |
.image |
Pull or cache an image |
.artifact |
Pull an OCI artifact |
.kube |
Run Kubernetes YAML with podman kube play |
This article focuses on .container. Per-type depth lives in sibling guides such as Podman Quadlet .container file explained and Run Podman pods with Quadlet. Kubernetes YAML through .kube is also covered in podman kube play context.
Generated service naming follows the basename:
web.container→web.serviceapp.network→app-network.servicedata.volume→data-volume.servicebackend.pod→backend-pod.service
Where Quadlet files are stored
Rootful search paths
Rootful Quadlet files are read from paths such as:
/run/containers/systemd/
/etc/containers/systemd/
/usr/share/containers/systemd/Later paths in the search order can be overridden by earlier ones depending on distribution policy. For administrator-created persistent rootful units, use:
/etc/containers/systemd/Rootless search paths
Rootless units are read from:
$XDG_RUNTIME_DIR/containers/systemd/
$XDG_CONFIG_HOME/containers/systemd/
~/.config/containers/systemd/
/etc/containers/systemd/users/${UID}/
/etc/containers/systemd/users/
/usr/share/containers/systemd/users/${UID}/
/usr/share/containers/systemd/users/Rootless lifecycle, lingering, and systemctl --user workflows belong in Rootless Podman Quadlet. Per-UID paths under /etc/containers/systemd/users/${UID}/ also underpin podmansh login shells that confine accounts inside a container at SSH login. This guide uses rootful examples under /etc/containers/systemd/.
Create your first .container Quadlet
Create quadlet-demo.container with four standard sections:
[Unit]
Description=Podman web container
[Container]
Image=registry.access.redhat.com/ubi9/ubi-minimal
ContainerName=quadlet-demo
Exec=sleep 3600
[Service]
Restart=on-failure
[Install]
WantedBy=multi-user.targetSave it on the lab host:
sudo tee /etc/containers/systemd/quadlet-demo.container > /dev/null <<'EOF'
[Unit]
Description=Podman web container
[Container]
Image=registry.access.redhat.com/ubi9/ubi-minimal
ContainerName=quadlet-demo
Exec=sleep 3600
[Service]
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOFSection roles:
[Unit] → standard systemd relationships and metadata
[Container] → Podman container configuration
[Service] → generated service behavior (restart, timeouts)
[Install] → boot dependencies interpreted by the generatorquay.io/podman/hello is a useful smoke-test image, but it prints once and exits. The lab uses ubi-minimal with Exec=sleep 3600 so the service stays running while you inspect status and logs.
Reload systemd after creating or editing Quadlet
Quadlet is a systemd generator. Editing quadlet-demo.container does not directly change an already-loaded quadlet-demo.service until the generator runs again.
Reload the systemd manager:
sudo systemctl daemon-reloaddaemon-reload exits silently on success. Confirm the generated unit exists:
sudo systemctl status quadlet-demo.serviceSample output:
○ quadlet-demo.service - Podman web container
Loaded: loaded (/etc/containers/systemd/quadlet-demo.container; generated)
Active: inactive (dead)The Loaded: ... (generated) line confirms systemd created the unit from your Quadlet file. inactive (dead) is normal before the first start.
Start the Quadlet service
Start the generated unit:
sudo systemctl start quadlet-demo.serviceCheck service state:
sudo systemctl status quadlet-demo.serviceSample output (trimmed):
● quadlet-demo.service - Podman web container
Loaded: loaded (/etc/containers/systemd/quadlet-demo.container; generated)
Active: active (running) since Sun 2026-08-23 02:13:30 IST; 1s ago
Main PID: 227868 (conmon)
CGroup: /system.slice/quadlet-demo.service
├─libpod-payload-...
│ └─227870 sleep 3600
└─runtimeThe service is quadlet-demo.service. The workload runs inside the Podman container quadlet-demo.
Confirm the container Podman created:
sudo podman ps --filter name=quadlet-demo --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'Sample output:
NAMES STATUS IMAGE
quadlet-demo Up 5 seconds registry.access.redhat.com/ubi9/ubi-minimal:latestQuadlet service name vs container name
For web.container, Quadlet always creates web.service.
The default Podman container name is systemd-web unless you set ContainerName=:
sudo tee /etc/containers/systemd/web.container > /dev/null <<'EOF'
[Unit]
Description=Default naming demo
[Container]
Image=registry.access.redhat.com/ubi9/ubi-minimal
Exec=sleep 3600
[Service]
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOFReload so systemd regenerates web.service from the new file:
sudo systemctl daemon-reloadStart the unit and compare the default Podman container name:
sudo systemctl start web.serviceList running containers to see the default name:
sudo podman ps --filter name=systemd-web --format 'table {{.Names}}\t{{.Status}}'Sample output:
NAMES STATUS
systemd-web Up 2 secondsWith ContainerName=web in [Container], the Podman container would be web, but the generated systemd unit would still be web.service.
Why systemctl enable does not work normally
Many administrators instinctively run:
sudo systemctl enable quadlet-demo.serviceSample output:
Failed to enable unit: Unit /run/systemd/generator/quadlet-demo.service is transient or generatedGenerated Quadlet services are transient. systemctl enable cannot create the usual persistent symlinks for them.
Put boot wiring in the Quadlet source instead:
[Install]
WantedBy=multi-user.targetAfter you add or change [Install], reload again:
sudo systemctl daemon-reloadCheck how systemd classifies the unit:
systemctl is-enabled quadlet-demo.serviceSample output:
generatedThe generated state is expected. Quadlet interprets supported [Install] keys such as WantedBy=, RequiredBy=, UpheldBy=, and Alias= when generating the unit.
Start a Quadlet automatically at boot
Rootful services commonly use:
[Install]
WantedBy=multi-user.targetRootless user services often target default.target instead. Match the target to the systemd scope that should pull the service in at boot.
Do not rely on systemctl enable. Configure [Install] in the .container file, run daemon-reload, and verify after reboot that the service starts without a manual systemctl start.
Restart containers with systemd
When systemd manages a Quadlet container, let systemd own restart behavior:
[Service]
Restart=always
RestartSec=5Do not add --restart=always through PodmanArgs= as the primary restart mechanism. Container restart policy and service restart policy should not fight each other.
Restart the running demo:
sudo systemctl restart quadlet-demo.servicesystemctl restart stops the old container and starts a fresh one according to the generated unit lifecycle.
View Quadlet logs
Service lifecycle messages go to the journal:
sudo journalctl -u quadlet-demo.serviceFollow live output:
sudo journalctl -u quadlet-demo.service -fContainer stdout and stderr are also available through Podman when the container is running:
sudo podman logs quadlet-demo| Tool | Shows |
|---|---|
journalctl -u name.service |
systemd unit lifecycle, generator events, conmon |
podman logs container |
container stdout/stderr |
Generator failures and missing units are covered in Podman Quadlet troubleshooting.
What happens when the service stops?
Stop the service:
sudo systemctl stop quadlet-demo.serviceCheck whether the container still exists:
sudo podman ps -a --filter name=quadlet-demo --format 'table {{.Names}}\t{{.Status}}'Sample output:
NAMES STATUSAn empty table means the generated service removed the container on stop. Quadlet-managed units create and tear down containers with the service lifecycle rather than treating a manually created CLI container as permanent state.
That model matters later for image updates and replacement workflows.
Quadlet service types
Generated units use different systemd service types depending on the Quadlet extension:
| Quadlet type | Typical systemd type |
|---|---|
.container, .kube |
notify (default) |
.pod |
forking |
.volume, .network, .build, .image, .artifact |
oneshot |
When systemctl status shows active (exited) on a volume or network unit, that is normal for a oneshot dependency service. Container units should show active (running) while the workload is up.
Preview what the generator will write:
sudo /usr/lib/systemd/system-generators/podman-system-generator --dryrunSample output (trimmed):
---quadlet-demo.service---
[Unit]
Description=Podman web container
SourcePath=/etc/containers/systemd/quadlet-demo.container
[X-Container]
Image=registry.access.redhat.com/ubi9/ubi-minimal
ContainerName=quadlet-demo
Exec=sleep 3600
[Service]
Restart=on-failure
ExecStart=/usr/bin/podman run ...Use the dry-run output on your host when you need the exact ExecStart, stop, and cleanup commands for your Podman version.
Long image pulls and TimeoutStartSec
A .container or .kube service can exceed systemd's normal startup timeout while Podman pulls or builds an image. Raise TimeoutStartSec= in [Service] or pre-pull the image. Resource Quadlets such as .volume, .network, .build, .image, and .artifact are oneshot units on Podman 5.8.2; their startup timeout is disabled by default and TimeoutStartSec does not apply in the same way.
[Service]
TimeoutStartSec=900Pre-pulling the image with podman pull avoids the timeout entirely. Do not add a large TimeoutStartSec to every unit without a reason.
Common Quadlet errors
| Symptom | Likely cause | Fix |
|---|---|---|
systemctl enable fails on generated unit |
Quadlet services are transient | Put WantedBy= in [Install] and run daemon-reload |
Container named systemd-web unexpectedly |
Default naming without ContainerName= |
Set ContainerName= or expect the systemd- prefix |
| Service not found after edit | Generator has not rerun | sudo systemctl daemon-reload |
| Changes do not appear | Service not restarted after reload | Restart name.service after daemon-reload |
| Unit missing entirely | Invalid directive in Quadlet file | Run podman-system-generator --dryrun and read generator errors |
Deeper debugging belongs in Podman Quadlet troubleshooting.
References
- Podman: systemd units using Quadlet
- Podman: Container Quadlet reference
- Podman: Manage Quadlets
- Podman: Deprecated generate systemd command
- RHEL 10: Porting containers to systemd using Podman
Summary
Podman Quadlet is a systemd generator, not a separate daemon. You write declarative files such as quadlet-demo.container under /etc/containers/systemd/, run daemon-reload, and manage the generated quadlet-demo.service unit with systemctl. The Podman container name is separate from the service name — expect systemd-<basename> unless you set ContainerName=.
The trap that catches experienced systemd users is systemctl enable. Generated Quadlet units are transient, so boot wiring belongs in [Install] WantedBy= inside the Quadlet source file. After every edit, reload systemd and restart the service; use journalctl -u for unit lifecycle logs and podman logs for container output.
For rootless paths, lingering, and user-manager workflows, continue with Rootless Podman Quadlet. For every [Container] directive, see Podman Quadlet .container file explained. To move off legacy podman generate systemd units, use Migrate from podman generate systemd to Quadlet.

