DEV Community

Cover image for Podman Quadlet: Modern systemd Integration
Maksym
Maksym

Posted on

Podman Quadlet: Modern systemd Integration

Quadlet replaces the old podman generate systemd approach with a cleaner, more declarative configuration system.

How Quadlet Works

Quadlet uses .container, .volume, .network, and .pod files that systemd automatically converts to service units at boot time.

File Locations

Place your Quadlet files in:

  • System-wide: /etc/containers/systemd/
  • User-specific: ~/.config/containers/systemd/

Basic Container Example

Create a file: /etc/containers/systemd/nginx.container

[Unit]
Description=Nginx Web Server
After=network-online.target
Wants=network-online.target

[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=/var/www/html:/usr/share/nginx/html:ro
Environment=MY_ENV_VAR=value

[Service]
Restart=always
TimeoutStartSec=900

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

Activate it:

# Reload systemd to discover the new unit
sudo systemctl daemon-reload

# Start the container
sudo systemctl start nginx

# Enable on boot
sudo systemctl enable nginx

# Check status
sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

Pod Example with Quadlet

Create: /etc/containers/systemd/webapp.pod

[Unit]
Description=Web Application Pod

[Pod]
PublishPort=8080:80
Network=webapp-net.network

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

Create: /etc/containers/systemd/webapp-nginx.container

[Unit]
Description=Nginx in webapp pod
Requires=webapp.pod
After=webapp.pod

[Container]
Image=nginx:latest
Pod=webapp.pod

[Service]
Restart=always

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

Create: /etc/containers/systemd/webapp-redis.container

[Unit]
Description=Redis in webapp pod
Requires=webapp.pod
After=webapp.pod

[Container]
Image=redis:latest
Pod=webapp.pod

[Service]
Restart=always

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

Activate the pod:

sudo systemctl daemon-reload
sudo systemctl start webapp.pod
sudo systemctl enable webapp.pod
Enter fullscreen mode Exit fullscreen mode

Network Example

Create: /etc/containers/systemd/webapp-net.network

[Unit]
Description=Web application network

[Network]
Subnet=10.89.0.0/24
Gateway=10.89.0.1
Enter fullscreen mode Exit fullscreen mode

Volume Example

Create: /etc/containers/systemd/data.volume

[Unit]
Description=Data volume

[Volume]
User=1000
Group=1000
Enter fullscreen mode Exit fullscreen mode

Complete Django + PostgreSQL Example

1. Network: /etc/containers/systemd/django-net.network

[Network]
Subnet=10.88.0.0/24
Enter fullscreen mode Exit fullscreen mode

2. Volume: /etc/containers/systemd/postgres-data.volume

[Volume]
Enter fullscreen mode Exit fullscreen mode

3. Database: /etc/containers/systemd/postgres.container

[Unit]
Description=PostgreSQL Database
After=network-online.target
Wants=network-online.target

[Container]
Image=docker.io/library/postgres:15
Volume=postgres-data.volume:/var/lib/postgresql/data
Network=django-net.network
Environment=POSTGRES_PASSWORD=secret
Environment=POSTGRES_USER=django
Environment=POSTGRES_DB=myapp

[Service]
Restart=always
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

4. Django App: /etc/containers/systemd/django.container

[Unit]
Description=Django Application
After=postgres.service
Requires=postgres.service

[Container]
Image=localhost/mydjango:latest
PublishPort=8000:8000
Network=django-net.network
Environment=DATABASE_HOST=postgres
Environment=DATABASE_NAME=myapp
Environment=DATABASE_USER=django
Environment=DATABASE_PASSWORD=secret
Volume=/app/media:/app/media
Volume=/app/static:/app/static

[Service]
Restart=always
TimeoutStartSec=300

[Install]
WantedBy=multi-user.target default.target
Enter fullscreen mode Exit fullscreen mode

Activate:

sudo systemctl daemon-reload
sudo systemctl enable --now postgres django
Enter fullscreen mode Exit fullscreen mode

Useful Quadlet Options

Container Section Options:

[Container]
Image=docker.io/library/nginx:latest
ContainerName=my-nginx
PublishPort=8080:80
PublishPort=443:443
Volume=/host/path:/container/path:ro
Volume=myvolume:/data
Environment=KEY=VALUE
EnvironmentFile=/path/to/env/file
Network=mynetwork.network
Pod=mypod.pod
User=1000
Group=1000
Exec=/custom/entrypoint.sh
AddCapability=NET_ADMIN
DropCapability=ALL
SecurityLabelDisable=true
Enter fullscreen mode Exit fullscreen mode

Service Section Options:

[Service]
Restart=always
RestartSec=10
TimeoutStartSec=900
Type=notify
NotifyAccess=all
Enter fullscreen mode Exit fullscreen mode

User Mode (Rootless) Quadlet

For running containers as a regular user:

1. Create directory:

mkdir -p ~/.config/containers/systemd
Enter fullscreen mode Exit fullscreen mode

2. Create: ~/.config/containers/systemd/myapp.container

[Unit]
Description=My User Application

[Container]
Image=myapp:latest
PublishPort=8080:8080

[Service]
Restart=always

[Install]
WantedBy=default.target
Enter fullscreen mode Exit fullscreen mode

3. Activate:

systemctl --user daemon-reload
systemctl --user enable --now myapp
loginctl enable-linger $USER  # Keep services running after logout
Enter fullscreen mode Exit fullscreen mode

Migration from Old Generate Systemd

Old way (deprecated):

podman generate systemd --name mycontainer > mycontainer.service
Enter fullscreen mode Exit fullscreen mode

New way with Quadlet:

  1. Create .container file instead
  2. Let systemd-generator handle the conversion
  3. Much cleaner and more maintainable

Advantages of Quadlet

Declarative configuration - Easy to read and maintain

Automatic dependency management - systemd handles ordering

Native systemd integration - Works like any other service

Git-friendly - Configuration files, not generated scripts

Supports all Podman features - Pods, networks, volumes

Better for GitOps - Store configs in version control

Checking and Debugging

# See generated service units
systemctl cat nginx

# Check logs
journalctl -u nginx -f

# Reload after changes
systemctl daemon-reload
systemctl restart nginx

# List all container services
systemctl list-units 'quadlet-*'
Enter fullscreen mode Exit fullscreen mode

This is the modern, recommended approach for managing Podman containers with systemd! 🚀

Feel free to criticize and share your opinions! Stay safe

Top comments (0)