Containers as a Service
This page explains how to run long-lived containers as rootless systemd user services using Podman Quadlet.
Where this applies
Section titled “Where this applies”This is the default deployment model for application backends documented under:
Exceptions (not containerized by default):
- Reverse proxy (NGINX):
web.core.lefand Proxy & routing - Internal DNS:
dns.core.lef - TCP proxy (HAProxy):
proxy.core.lefand TCP proxy - Tooling DB backend:
alma.core.lef(seetools.db.lef)
Why we use this
Section titled “Why we use this”- Services restart automatically (systemd handles lifecycle).
- Containers run without root privileges.
- Deployments are file-based and auditable (
.containerunit files).
Prerequisites
Section titled “Prerequisites”- Podman installed on the host.
- systemd user sessions enabled (
loginctl enable-linger). - If SELinux is enabled (RHEL/Alma/Rocky), use
:Zon bind mounts where appropriate.
Create a dedicated service user (one-time)
Section titled “Create a dedicated service user (one-time)”Run as root:
useradd -m -s /bin/bash <service_user>
passwd <service_user>
loginctl enable-linger <service_user>Optional (if you want to read system logs):
usermod -aG systemd-journal <service_user>Define a Quadlet unit (.container)
Section titled “Define a Quadlet unit (.container)”As the service user, create a unit in:
~/.config/containers/systemd/
Example:
[Unit]
Description=My internal service
After=network-online.target local-fs.target
[Container]
ContainerName=myservice
Image=docker.io/your/image:latest
Network=slirp4netns:allow_host_loopback=true
PublishPort=8080:80
Volume=/home/<service_user>/myservice/config:/etc/myservice/config:Z
Volume=/home/<service_user>/myservice/data:/var/lib/myservice:Z
Environment=MY_ENV_VAR=value
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.targetStart and manage the service
Section titled “Start and manage the service”systemctl --user daemon-reload
systemctl --user enable --now myservice.service
systemctl --user status myservice.serviceSystemd unit logs:
journalctl --user -u myservice.service -fApplication logs (container):
podman ps
podman logs myserviceTroubleshooting
Section titled “Troubleshooting”Validate a unit file (path may vary per distro):
/usr/libexec/podman/quadlet -dryrun -user ~/.config/containers/systemd/myservice.containerKnown risks / failure modes
Section titled “Known risks / failure modes”- Bind mounts must use full paths; relative paths are not supported.
- Services won’t start after reboot if linger isn’t enabled for the service user.
- Port binds may fail if the port is already in use or blocked by policy.