How to use Quadlet on Bazzite

March 19 2025 9:49pm • Est. Read Time: 4 MIN

Quadlet 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:

OptionExampleDescription
ContainerNameContainerName=nginxName of the container.
ImageImage=docker.io/nginxinc/nginx-unprivilegedContainer image to be used.
PublishPortPublishPort=8080:8080Port mapping in the format HOST_PORT:CONTAINER_PORT.
VolumeVolume=/path/to/data:/data:zBind mount a host folder into the container. The :z option prevents SELinux from blocking access.
NetworkNetwork=hostNetwork 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.