The Shift from Docker Compose to Native Systemd Services
Docker Compose was my reliable workhorse for years. It is simple, the YAML syntax is intuitive, and it just works. However, as my lab expanded to over 15 services, I hit a wall. Managing Docker as a root-level daemon felt like an unnecessary security risk. Worse, ensuring containers started in the right order after a power outage often required brittle shell scripts and “sleep” commands.
I eventually moved to Podman and its secret weapon: Quadlets. Unlike Docker, Podman is daemonless. It excels at running “rootless” containers, which means your services run under a standard user account without administrative privileges. Quadlets simplify this by letting you define containers using a syntax that Systemd understands natively. You write a short configuration file, and Systemd handles the heavy lifting—logging, auto-starts, and complex dependency mapping.
After implementing this in my own setup, the stability was immediate. My Raspberry Pi 4 boot time dropped by nearly 15 seconds because Systemd parallelizes service starts more efficiently than the Docker daemon ever could. Integration with standard Linux monitoring tools is now seamless.
Setting Up Podman on Your System
Most modern Linux distributions include Podman in their official repositories. If you use Ubuntu 22.04, Fedora, or Debian, you are ready to go. I recommend using Podman version 4.4 or higher. This version, released in early 2023, introduced significant stability updates for Quadlets.
# For Ubuntu/Debian
sudo apt update
sudo apt install podman -y
# For Fedora
sudo dnf install podman -y
Check your version after the installation finishes:
podman --version
To run containers the “HomeLab way” (non-root), verify that your user has a subuid and subgid range. These ranges allow Podman to map internal container users to specific IDs on your host machine without overlapping with system users.
cat /etc/subuid
cat /etc/subgid
If the output shows your username followed by a range like 100000:65536, your environment is configured correctly.
Converting Docker Compose to Quadlet Files
In a Docker Compose setup, you typically rely on a docker-compose.yml file. With Quadlets, we switch to .container files. Let’s look at a standard Nginx web server configuration with a persistent volume.
The Compose Baseline
services:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:Z
restart: always
The Quadlet Equivalent: nginx.container
Rootless Quadlet files belong in ~/.config/containers/systemd/. Create that directory and add a file named nginx.container:
[Unit]
Description=Nginx Web Server
After=network-online.target
[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=%h/html:/usr/share/nginx/html:Z
[Install]
WantedBy=default.target
The [Container] section combines Systemd logic with Podman arguments. I used the %h specifier here, which dynamically points to your home directory. This makes your config files portable across different user accounts. If you use Fedora or RHEL, the :Z flag is vital; it tells SELinux to relabel the files so the container can actually read them.
Managing Networks and Volumes
Sometimes you need isolated networks to keep a database away from the public internet. You can define these using .network files. For a backend network, create db-net.network:
[Network]
NetworkName=backend-network
Link it to your service by adding Network=db-net.network inside your .container file. It is clean and modular.
Running and Monitoring Your New Services
The workflow changes slightly here. You no longer use podman run manually. Instead, you ask Systemd to process the Quadlet files and generate the background service units.
Reload the user daemon to register the changes:
systemctl --user daemon-reload
Your nginx.container is now a standard service. You can control it with the same commands you use for any other Linux process:
# Start the container
systemctl --user start nginx
# Enable it to start automatically on boot
systemctl --user enable nginx
# Check if it's running
systemctl --user status nginx
Centralized logging is the biggest win. Because these are native units, all logs flow directly into journald. You don’t need proprietary Docker commands to see what went wrong. Just use journalctl:
journalctl --user -u nginx -f
Keeping Services Alive
Rootless services usually die when you log out of your SSH session. To keep your HomeLab running 24/7, you must enable “lingering” for your user account. This allows the user’s Systemd instance to start at boot and stay active.
loginctl enable-linger $USER
Verification and Troubleshooting
If a service crashes, check the generated unit file first. Quadlet converts your high-level config into a real Systemd unit located in /run/user/1000/systemd/generator/. To preview the conversion without applying it, use the dry-run tool:
/usr/libexec/podman/quadlet -dryrun -user
This command is a lifesaver for catching syntax errors or broken file paths before they cause a headache. For real-time resource tracking, podman stats still provides a quick look at CPU and RAM usage across all running containers.
Switching to Quadlets treats your containers as native OS services. You get the robust dependency management of Systemd—like ensuring your app waits for the database—paired with Podman’s secure architecture. It is a professional upgrade that makes any HomeLab significantly more resilient.

