How to Run Rootless Containers on Ubuntu 24.04 with Podman and systemd Quadlet (Auto-Updates Included)
Overview
Running containers without root is a big security win, and Ubuntu 24.04 LTS makes it easy with Podman and systemd Quadlet. Podman is a Docker-compatible container engine that works without a daemon, and Quadlet lets you define containers as systemd units for reliable startup, health checks, logging, and auto-updates. In this tutorial, you will set up a rootless container managed by systemd, enable automatic image updates, and learn how to monitor and troubleshoot the service.
Prerequisites
You will need an Ubuntu 24.04 LTS system, a regular user with sudo rights, outbound internet access to pull images, and an open application port above 1024 (rootless containers cannot bind to privileged ports). This guide assumes your username is $USER and that you will host a simple HTTP service on port 8080.
Step 1: Install Podman
Ubuntu 24.04 includes a recent Podman package. Install it via APT and verify the version:
sudo apt update
sudo apt install -y podman
podman --version
podman info
If podman info shows cgroup version 2 and a working network backend, you are good to go. No system-wide daemon is needed.
Step 2: Enable user lingering for systemd
To let your user services keep running after logout, enable lingering and confirm that the user systemd instance is active:
sudo loginctl enable-linger "$USER"
systemctl --user status
If the second command shows a status page, the per-user systemd is ready. Otherwise, log out and back in, or start it by running any user service.
Step 3: Create a Quadlet unit for a sample web app
Quadlet reads unit files from ~/.config/containers/systemd/ and generates corresponding systemd services. Create the directory and a container unit that runs a tiny HTTP server (traefik/whoami) on port 8080:
mkdir -p ~/.config/containers/systemd
cat > ~/.config/containers/systemd/whoami.container <<'EOF'
[Unit]
Description=Rootless whoami demo (Podman + Quadlet)
After=network-online.target
Wants=network-online.target
[Container]
# Image to run
Image=traefik/whoami:latest
# Name the container consistently
ContainerName=whoami
# Map host port 8080 to container port 80
PublishPort=8080:80
# Persist simple state/logs if needed
Volume=%h/containers/whoami:/data:Z
# Health check for systemd readiness
HealthCmd=curl -fsS http://127.0.0.1:80/ || exit 1
HealthInterval=30s
# Timezone for logs
Environment=TZ=UTC
# Auto update from registry when new image is available
AutoUpdate=registry
# Pass any extra runtime args if needed
# ContainerRuntimeArguments=--pids-limit=256
[Service]
# Restart on failure
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
EOF
The AutoUpdate=registry directive is the Quadlet-native way to enable image updates. The PublishPort setting maps host port 8080 to the container’s port 80. The HealthCmd allows systemd to consider the service healthy only after the endpoint responds.
Step 4: Generate and start the systemd service
Reload the user systemd manager to pick up the new Quadlet file, then enable and start the service:
systemctl --user daemon-reload
systemctl --user enable --now whoami.service
systemctl --user status whoami.service
You should now see the service active. Verify the container is running and accessible:
podman ps
curl -s http://127.0.0.1:8080/
The curl output returns headers that include your container’s hostname, confirming the service is up.
Step 5: Enable automatic image updates
Podman provides a built-in timer to auto-update containers based on Quadlet’s AutoUpdate=registry or the io.containers.autoupdate label. Enable the timer:
systemctl --user enable --now podman-auto-update.timer
systemctl --user list-timers | grep podman-auto-update
You can test an update cycle without applying it:
podman auto-update --dry-run
When a new image is available, Podman will pull it and restart only the affected containers, minimizing downtime.
Step 6: Logs, troubleshooting, and lifecycle
To review live logs and diagnose issues, use journalctl and Podman’s own logs. Here are the most useful commands:
journalctl --user-unit whoami.service -f
podman logs -f whoami
systemctl --user restart whoami.service
systemctl --user stop whoami.service
If you change the .container file, always run systemctl --user daemon-reload and then systemctl --user restart whoami.service to apply changes. To remove the service completely, stop and disable it, delete the Quadlet file, and reload:
systemctl --user disable --now whoami.service
rm ~/.config/containers/systemd/whoami.container
systemctl --user daemon-reload
podman rm -f whoami
Step 7: Exposing the service on your network
If you plan to expose the service beyond localhost, open the firewall and bind to the host’s address. With UFW, allow port 8080:
sudo ufw allow 8080/tcp
For production, consider putting this behind a reverse proxy such as Caddy, Nginx, or Traefik for HTTPS termination and rate limiting. Rootless containers cannot bind to ports below 1024, so use a reverse proxy on 443/80 or use auth/stream termination upstream.
Step 8: Tips for production use
Use bind mounts or volumes for persistent data, set resource limits with ContainerRuntimeArguments (CPU/memory/pids), and prefer pinned tags or digests for image stability. For private registries, log in with podman login <registry> and consider CredentialHelpers. Finally, keep Ubuntu patched (using unattended-upgrades), and monitor your services with systemd health checks and alerts.
Conclusion
You have created a secure, rootless container on Ubuntu 24.04 using Podman and systemd Quadlet, enabled automatic image updates, and learned how to manage the service lifecycle with systemd. This pattern makes container workloads feel like first-class system services, while keeping your host safer by avoiding root privileges.
Comments
Post a Comment