How to use Quadlet on Bazzite
March 19 2025 9:49pm • Est. Read Time: 4 MINQuadlet is a feature of Podman that allows a user to run a container as a systemd unit. It works by using a declarative syntax similar to Docker Compose to define container configurations, but instead of directly launching containers, it converts those configurations into systemd unit files. This integration with systemd means you can manage your containerized applications using familiar systemctl commands, seamlessly incorporating container management into your system’s startup and service management.
Why Use Quadlet?
Bazzite’s design emphasizes an Atomic base system for stability and security. However, there is often a need to run dynamic services – such as containerized applications – without modifying the core image. Quadlet provides several benefits:
- Declarative Service Management: Write a simple configuration file instead of manually crafting systemd unit files.
- Seamless systemd Integration: Containers are managed as systemd units, so you can use familiar commands (start, stop, enable, status).
- Podman Backend: Uses Podman to run containers, avoiding the need for a Docker daemon and providing a secure, rootless option.
- Atomic and Reproducible Changes: Changes are applied atomically, ensuring that service updates are reliable and easily reversible.
How Quadlet Works
Quadlet reads container configuration files written in a declarative format – much like Docker Compose files – and automatically converts them into systemd unit files. Once generated, these unit files are managed via systemd, which means you can start, stop, and enable your containers just like any other system service.
Quick Example
The following example demonstrates how to run an NGINX container as a systemd unit using Quadlet.
Step 1: Create the Container Configuration File
Create a file called ~/.config/containers/systemd/nginx.container
with the following content:
[Container] ContainerName=nginx Image=docker.io/nginxinc/nginx-unprivileged PublishPort=8080:8080
This configuration tells Quadlet to set up a container named nginx
using the nginx-unprivileged
image from Docker Hub, with port 8080 published.
Step 2: Reload and Start the Service
Save the configuration file, then run the following commands to reload your user systemd manager and start the container as a service:
systemctl --user daemon-reload systemctl --user start nginx
Step 3: Access the Containerized Service
To verify that your container is running correctly, open your web browser and navigate to:
xdg-open localhost:8080
This command opens your default browser to http://localhost:8080
(which corresponds to the IP address 127.0.0.1). You should see the default NGINX welcome page, indicating that the container is active and accessible.
Use Cases
Quadlet can be used for running containerized applications such as web servers, media servers, databases, or any other service packaged as a container.
Managing Quadlet
Quadlet-managed containers can be controlled like any other systemd service. For example:
- Checking Quadlet Status:
systemctl --user status nginx
- Stopping Quadlet:
systemctl --user stop nginx
You can find more commands in the systemctl man pages or by using tldr systemctl
.
Quadlet File Locations
Quadlet configuration files can be placed in several locations, sorted by priority:
$XDG_RUNTIME_DIR/containers/systemd/
– Usually used for temporary Quadlet configurations.~/.config/containers/systemd/
– Recommended location for user-specific configurations./etc/containers/systemd/users/$(UID)
/etc/containers/systemd/users/
If you want your service to start even when you are not logged in, run loginctl enable-linger $USER
to have it start automatically.
Running Quadlet on Startup
To have your container service start automatically on startup, add an [Install]
section to the Quadlet configuration file. Most of the time, setting:
[Install] WantedBy=default.target
is sufficient. For example:
[Container] ContainerName=nginx Image=docker.io/nginxinc/nginx-unprivileged PublishPort=8080:8080 [Install] WantedBy=default.target
Note: You don't need to run systemctl enable
because the service files are generated automatically, and you cannot run that command manually.
Converting Docker Compose to Quadlet Unit
Many containerized applications on the web are built using Docker Compose. To run these applications as Quadlet units, you need to convert the Compose file into the Quadlet format. Fortunately, you can use the podlet
utility to assist with this conversion.
By default, Quadlet requires the full repository name. For example, if the image is "nginxinc/nginx-unprivileged", you should use "docker.io/nginxinc/nginx-unprivileged".
Running Rootful Containers as Quadlet
Ideally, you would run all containers using rootless Podman. However, some containers require root privileges. For example, this guide uses nginx-unprivileged
instead of the normal nginx because the latter needs root access to function properly.
To use rootful Podman, you must use a different Quadlet path and run the service using root systemctl (without the --user
flag). Rootful Quadlet paths include:
/run/containers/systemd/
– Temporary Quadlet configurations./etc/containers/systemd/
– Recommended location./usr/share/containers/systemd/
– Typically used for image-defined configurations.
Common Quadlet Key Descriptions
Below are some common keys used in Quadlet configuration files:
Option | Example | Description |
---|---|---|
ContainerName | ContainerName=nginx | Name of the container. |
Image | Image=docker.io/nginxinc/nginx-unprivileged | Container image to be used. |
PublishPort | PublishPort=8080:8080 | Port mapping in the format HOST_PORT:CONTAINER_PORT. |
Volume | Volume=/path/to/data:/data:z | Bind mount a host folder into the container. The :z option prevents SELinux from blocking access. |
Network | Network=host | Network mode for the container (host, none, or a user-defined network). |
Advanced Features
Quadlet supports advanced usage scenarios that are particularly useful for workstations and production environments:
- Integration with System Updates: Quadlet’s declarative approach ensures your container configurations remain consistent across system updates.
- How it works: The configuration files managed by Quadlet are stored separately from the core, Atomic system. As a result, when system updates occur (e.g., via rpm-ostree), your container definitions persist without being overwritten. This means that your service behavior remains consistent even after the underlying system is updated.
Examples
Minecraft Server
Documentation: https://docker-minecraft-server.readthedocs.io/en/latest
[Container] ContainerName=minecraft Environment=EULA=TRUE Image=docker.io/itzg/minecraft-server PublishPort=25565:25565 Volume=/path/to/data:/data:z [Install] WantedBy=default.target
Use absolute paths for volumes (e.g., /home/username/minecraft/data
).
Plex Server
Documentation: https://github.com/plexinc/pms-docker
[Container] ContainerName=plex Environment=TZ=Your/TimeZone Image=docker.io/plexinc/pms-docker Network=host Volume=/path/to/config:/config:z Volume=/path/to/transcode:/transcode:z Volume=/path/to/media:/data:z [Install] WantedBy=default.target
Use absolute paths for volumes (e.g., /home/username/plex/config
). You can mount multiple volumes for different media types if needed.
Summary
Quadlet is an essential tool on Bazzite for running containers as native systemd services. It converts declarative configuration files into systemd unit files using Podman as a backend, allowing you to manage containerized applications with familiar systemd commands. Whether you're deploying an NGINX server, a media server, or any other containerized service, Quadlet provides a robust, reproducible, and easily manageable way to integrate container management into your Bazzite system.
With features such as versioned configurations, rollback mechanisms, and seamless integration with system updates, Quadlet empowers you to experiment safely with container configurations while ensuring that your services remain consistent and reliable across system upgrades.