Quadlet (Services)¶
Quadlet Usecases¶
Quadlet is a feature of podman that allows a user to run a container as systemd units. It works by using a declarative syntax like docker compose but integrates to systemd and uses podman as a backend.
Quadlet can be used for application packaged as a container such as a server application. You can find a lot of examples of containerized applications from Linux Server.
Managing Quadlet¶
Quadlet can be managed like any other systemd service using below command.
Checking Quadlet status
systemctl --user status <service>
Stopping Quadlet
systemctl --user stop <service>
You can see more commands in man systemctl or tldr systemctl.
Note
Do not add the .container suffix when you interact with systemctl or an error will occur.
Quadlet File Locations¶
You can put your quadlet in these locations sorted by priority:
$XDG_RUNTIME_DIR/containers/systemd/- Usually used for temporary quadlet~/.config/containers/systemd/- Recommended location/etc/containers/systemd/users/$(UID)/etc/containers/systemd/users/
Note
If you want your service to start even when you are not logged in, run loginctl enable-linger $USER to start it automatically.
Running Quadlet on Startup¶
You may want to run your quadlet automatically on startup, just add an install section to the quadlet file if you want it to autostart. Most of the time default.target is what you want but if you need other target you can read about that on systemd docs.
[Install]
WantedBy=default.target
Converting Docker Compose to Quadlet Unit¶
You will find that most of containerized apps in the web are built using docker compose. Even the Linux Server that is linked above has all containers documented using a compose file. So you will need to convert it first, before running it as quadlet, fortunately you can use podlet to help you converting it.
Note
By default quadlet require full repository name. Most images are in docker hub so add docker.io/ (e.g "nginxinc/nginx-unprivileged" becomes "docker.io/nginxinc/nginx-unprivileged") to it.
Running Rootful Container as Quadlet¶
While ideally you would run all containers using rootless podman, unfortunately not all containers will work with it. Use rootful podman by using a different quadlet path and run using root systemctl (without --user).
Rootful Quadlet Paths
- /run/containers/systemd/ - Temporary quadlet
- /etc/containers/systemd/ - Recommended location
- /usr/share/containers/systemd/ - Image defined
Common Quadlet Key Description¶
| Option | Example | Description |
|---|---|---|
| ContainerName | ContainerName=nginx | Name of the container. |
| Image | Image=docker.io/nginxinc/nginx-unprivileged | Container image that you want to use. |
| AutoUpdate | AutoUpdate=registry | Source to check for update. The value is either registry or local. |
| PublishPort | PublishPort=8080:8080 | Port opened by container. (HOST_PORT:CONTAINER_PORT) |
| Volume | Volume=/path/to/data:/data:z | Link host folder with container folder. (HOST_FOLDER:CONTAINER_FOLDER:OPTION) |
| Network | Network=host | Network used by container. The value can be host, none, or user defined network name |
Note
The z option in volume is to prevent selinux from blocking access to the folder. You can read more here.
Troubleshooting¶
If your quadlet for some reason isn't found or starting, you can debug the container unit using /usr/libexec/podman/quadlet -dryrun for system quadlet or /usr/libexec/podman/quadlet -user -dryrun for user quadlet.
Examples¶
Real world examples for Quadlet usage.
Minecraft Server Hosting¶
Note
Don't forget to run systemctl --user daemon-reload after creating the file
Documentation: https://docker-minecraft-server.readthedocs.io/en/latest
Quadlet File:
# ~/.config/containers/systemd/minecraft.container
[Container]
ContainerName=minecraft
Environment=EULA=TRUE
Image=docker.io/itzg/minecraft-server
AutoUpdate=registry
PublishPort=25565:25565
Volume=/path/to/data:/data:z
# Remove if you don't want autostart
[Install]
WantedBy=default.target
Note
Use absolute path for volume, e.g /home/username/minecraft/data.
NGINX Web Server¶
Create a file called ~/.config/containers/systemd/nginx.container with content below.
[Container]
ContainerName=nginx
Image=docker.io/nginxinc/nginx-unprivileged
AutoUpdate=registry
PublishPort=8080:8080
Save it and run the code below.
systemctl --user daemon-reload
systemctl --user start nginx
xdg-open localhost:8080
Plex Media Server¶
Documentation: https://github.com/plexinc/pms-docker
Quadlet File:
# ~/.config/containers/systemd/plex.container
[Container]
ContainerName=plex
Environment=TZ=Your/TimeZone
Image=docker.io/plexinc/pms-docker
AutoUpdate=registry
Network=host
Volume=/path/to/config:/config:z
Volume=/path/to/transcode:/transcode:z
Volume=/path/to/media:/data:z
# Remove if you don't want autostart
[Install]
WantedBy=default.target
Note
You can find list of timezones here.
Note
Use absolute path for volume, e.g /home/username/plex/config.
Note
You can mount multiple volumes for your media, e.g Volume=/path/to/media:/tv:z and Volume=/path/to/another/media:/movie:z. Consult the documentation for more info.
Video Tutorial¶
Samba Server¶
Documentation: https://github.com/ServerContainers/samba
Quadlet File:
# /etc/containers/systemd/samba.container
[Container]
Environment=ACCOUNT_username=password
# Protected share with write access
Environment="SAMBA_VOLUME_CONFIG_protected=[My Share]; path=/shares/protected; valid users = username; guest ok = no; read only = no; browseable = yes"
# Open share with readonly access
Environment="SAMBA_VOLUME_CONFIG_guest=[Guest Share]; path=/shares/guest; guest ok = yes; browseable = yes"
Image=ghcr.io/servercontainers/samba:smbd-only-latest
AutoUpdate=registry
Network=host
Volume=/path/to/protected:/shares/protected:z
Volume=/path/to/guest:/shares/guest:z
# Remove if you don't want autostart
[Install]
WantedBy=default.target
Note
You can find list of timezone here.
Note
Use absolute path for volume, e.g /home/username/samba/guest.
