Quadlet is a more robust and idiomatic way to manage Podman containers with systemd than the older podman generate systemd approach.

Let’s see Quadlet in action. Imagine you have a simple web server container you want to run.

First, install podman-compose if you haven’t already, as it can help generate initial Quadlet files from existing docker-compose.yml or compose.yaml files.

sudo dnf install podman-compose -y

Now, let’s say you have a docker-compose.yml like this:

version: '3.8'
services:
  my-webserver:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html

You can generate a Quadlet file from this:

mkdir -p ~/.config/containers/systemd
podman-compose --podman-only --quadlet-output ~/.config/containers/systemd convert

This will create a file named my-webserver.service (or similar, depending on your service name) in ~/.config/containers/systemd/.

The generated my-webserver.service file might look something like this:

[Unit]
Description=my-webserver
Wants=network-online.target
After=network-online.target

[Service]
Environment="PODMAN_SYSTEMD_UNIT=my-webserver.service"
Delegate=yes
KillMode=none
Type=forking
GuessGCCC = 1
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/podman run --name my-webserver --label "io.podman.compose.project=my-compose-project" --label "io.podman.compose.version=1.0.0" --label "com.docker.compose.service=my-webserver" --label "com.docker.compose.container-number=1" --label "com.docker.compose.config-hash=..." -p 8080:80 -v /path/to/your/html:/usr/share/nginx/html:Z nginx:latest
ExecStop=/usr/bin/podman stop --ignore --cidfile=/var/run/my-webserver.cid
ExecStopPost=/usr/bin/podman rm --ignore --cidfile=/var/run/my-webserver.cid
PIDFile=/var/run/my-webserver.cid

This .service file is directly usable by systemd. Quadlet takes the container configuration and translates it into a systemd unit file.

Here’s how it works internally: Quadlet reads container definitions (either from .container or .kube files, or generates .service files from podman-compose convert). It then creates corresponding systemd unit files that instruct podman to run, stop, and manage these containers as system services. The [Unit] section defines dependencies, and the [Service] section contains the actual podman run commands and lifecycle management.

The key advantage is that systemd now has direct control. You can start, stop, enable, and disable your containers using standard systemd commands:

sudo systemctl start my-webserver.service
sudo systemctl enable my-webserver.service
sudo systemctl status my-webserver.service
sudo systemctl stop my-webserver.service

This integrates seamlessly with systemd’s features like dependency management, automatic restarts, and logging via journalctl.

The ExecStart line is where the magic happens. It’s a direct podman run command, but systemd ensures it’s executed reliably. The Delegate=yes option is crucial; it tells systemd that the service manager (Podman, in this case) is responsible for managing child processes, preventing systemd from trying to manage the container itself. Type=forking is often used because podman run forks a process that becomes the container, and the original podman run command exits. PIDFile and cidfile are used to track the container’s process ID.

The most surprising thing about Quadlet is how it bridges the gap between declarative container definitions and imperative system service management without requiring a separate orchestration layer for simple use cases. You define your container, and systemd treats it as a first-class service.

If you’re using podman generate systemd and manually tweaking the output, you’re missing out on the cleaner, more integrated approach Quadlet provides. It’s designed to be the modern, systemd-native way to manage Podman containers.

The next logical step is to explore managing multiple related containers as a single Podman Pod, which Quadlet also supports with .pod files.

Want structured learning?

Take the full Podman course →