podman
Overview: StandaloneAll container30 workloadscontainerized services (40 containers total) on ul run exclusively under rootless Podman (podman 6.) managed as native x1.4systemd --user user services via Podman Quadlets under the dedicated unprivileged account docker-data (UID 1100, GID 1100). ThisRootful eliminatesDocker (docker.service, docker.socket, and containerd.service) is completely disabled and decommissioned, eliminating the privileged root container daemon attack surface while preserving the per-service ULA IPv6 reverse-proxy architectureisolation on eth77.
1. Architecture & Security Comparison: Rootless Podman vs. Rootful Docker
Under traditional Docker, dockerd runsand containerd execute permanently as root (UID 0) inside the host init user namespace (user:[4026531837]). Every container spawned by dockerd without user-namespace remapping shares thekernel host kernel's root UID 0 whenever the container process runs as root, mutates host iptables/nftables chains directly as root, and anyrequires vulnerabilityroot insocket theaccess daemon(/var/run/docker.sock) for orchestration or runtimemaintenance starts with full host privileges.timers.
Under rootless Podman,Podman on ul, there is no central root container daemon anywhere on the host.:
systemdmanagesstartsaanper-unprivileged userservicemanager (user@1100.service)via lingering) fordocker-data,.
.container unit directly spawns lightweight conmon and crun processesuser:[4026534031]).
Shared stateful databases (PostgreSQL on 127.0.0.1:5432 and Valkey on 127.0.0.1:6379) run natively on the host locked strictly to loopback, with zero LAN or Docker bridge exposure.
1.1 User Namespace UID/GID Translation Model
Rootless Podman mapstranslates container UIDs and GIDs into unprivileged host IDs using /etc/subuid and /etc/subgid (docker-data:624288:65536). Under the default rootless mapping (UserNS=host within the rootless user namespace),mapping, any container UID $u \in [0, 65536]$ translatesmaps deterministically to a host kernel UID according to:UID:
$$ \text{UID}_{\text{host}}(u) = \begin{cases} 1100 & \text{if } u = 0 \quad (\text{container } \texttt{root} \longrightarrow \text{host } \texttt{docker-data}) \cr 624287 + u & \text{if } 1 \le u \le 65536 \quad (\text{subordinate range } 624288\text{--}689823) \end{cases} $$
Common live worker translations visible in ps / podman-map-tree on ul:
root (u = 0) $\longrightarrow$ Host UID 1100 (docker-data) with zero host capabilities.
Container www-data (u = 33, e.g., nextcloud, yourls, webrcade) $\longrightarrow$ Host UID 624320.
Container nginx (u = 101, e.g., audiomass, bentopdf, excalidraw, penpot-frontend, tldraw, wick) $\longrightarrow$ Host UID 624388.
Container postgres (u = 999, e.g., penpot-postgres) $\longrightarrow$ Host UID 625286.
Container authentik / degoog (u = 1000) $\longrightarrow$ Host UID 625287.
Container penpot (u = 1001) $\longrightarrow$ Host UID 625288.
When a servicebinary explicitly sets UserNS=keep-id with User=1100:1100 (such as drawpile, whose binary refuses to start if internal getuid() == 0 (specifically drawpile-srv), thesetting identityUserNS=keep-id mappingwith User=1100:1100 preserves UID 1100 across the namespace boundary:
$$ \text{UID}_{\text{keep-id}}(1100) \stackrel{\texttt{userns}}{\longleftrightarrow} 1100 \quad (\texttt{docker-data}) $$
Why Default Rootless UserNS Beats UserNS=keep-id for Large ROCm Images: Avoid setting UserNS=keep-id unless an application explicitly aborts when getuid() == 0. On multi-gigabyte images such as immich-machine-learning:v3.3.1-rocm (23.3 GB) or subocr, UserNS=keep-id triggers a full recursive ownership copy-up across thousands of Python/ROCm files that stalls startup for over 5 minutes. Under the default rootless user namespace, container UID 0 already maps directly to host docker-data (1100:1100) with zero layer copy-up and sub-2-second startup.
1.2 Side-by-Side Comparison
| Dimension | Legacy Rootful Docker (dockerd) |
Current Rootless Podman (docker-data Quadlets) |
|---|---|---|
| Daemon Model | Persistent root dockerd + containerd) running as UID 0 |
Daemonless fork/exec ( $\longrightarrow$ conmon $\longrightarrow$ crun) running as UID 1100 |
Host Kernel UID of Container root |
Host UID 0 (root in init user:[4026531837]) |
Host UID 1100 (docker-data in isolated user:[4026534031]) |
Host Kernel UID of Container Worker (u > 0) |
Same numeric host UID 33 collides with host http, |
624288--689823, zero |
| Service Definition & Lifecycle | managed by dockerd |
Declarative <svc>.container + <svc>.network Quadlets in /data/docker/<svc>/ |
|
Cached policy |
WAN-outage-safe boot |
| Host Firewall Mutation | Mutates host iptables/nftables chains root |
Zero host firewall mutation; eth77 ULA |
389 / 636 LDAP/LDAPS)
Direct root port bind in container + mounted /etc/nginx/certs
Rootless authentik-ldap on :3389/:6636 fronted by host HAProxy TCP passthrough (/etc/haproxy/conf.d/ldap.cfg); zero TLS key mounts in containers
Shared Host PostgreSQL & Valkey
Exposed on Docker bridges (172.16.0.0/12) + LAN (10.0.0.0/24)
Locked strictly to 127.0.0.1 / ::1; reached from containers via pasta --map-host-loopback 169.254.1.2
ROCm GPU Access (/dev/kfd, /dev/dri)
devices: + group_add: ["91", "991"]group_add as root
AddDevice= + GroupAdd=keep-groups inheriting docker-data's video (91) and render (991) groups
/var/lib/docker/overlay2root:root/home/docker-data/.local/share/containers/storageoverlaydocker-data2. Host Configuration & Intra-Service Networking on ul
2.1 Unprivileged Account, Subordinate IDs & Systemd Lingering
- Service Account (
docker-data):- Primary UID/GID:
1100:1100(docker-data)
- Primary UID/GID:
Home Directory:home/home/docker-data.- Supplementary Groups:
video(91) andrender(991) for AMD Radeon AI PRO R9700 (gfx1201) ROCmGPU devicecompute access (/dev/driand/dev/kfd). - Subordinate UID/GID Ranges (
/etc/subuid&/etc/subgid):docker-data:624288:65536 - Systemd
UserLingeringLingering& Automatic Maintenance:Enabledvia - Lingering is enabled at
/var/lib/systemd/linger/docker-data(sudo loginctl enable-linger docker-data)(creating/var/lib/systemd/linger/docker-data). This ensuressouser@1100.servicestarts automatically atsystembootand keeps all rootless Podman Quadlet services runningwithoutrequiringanactiveinteractiveloginlogin.
/etc/systemd/system/podman-prune.{service,timer} (User=docker-data, Sundays at 04:00 EDT/EST), replacing the retired docker-prune.timer.
2.2 Global Configuration Files & Storage Layout
/etc/containers/: Configuresregistries.containers.confunqualifiedpastaimage(passt)namesas the default rootless network backend and maps the synthetic guest address169.254.1.2(host.containers.internal) cleanly toresolvehostagainst127.0.0.1Dockerloopback:Hub[network]bydefault_rootless_network_cmddefault:= "pasta" pasta_options = ["--map-host-loopback", "169.254.1.2"]
/etc/containers/registries.conf:
unqualified-search-registries = ["docker.io"]
/etc/containers/containers.conf[network]
default_network_backend = "pasta"
pasta_options = ["-a", "10.0.2.100", "-a", "fd00::100"]
Storage Paths:
- Persistent image/layer store (
GraphRoot):/home/docker-data/.local/share/containers/storage(native kerneloverlayfilesystemdriveronownedNVMeby)./docker-data - Ephemeral runtime state & DNS tables (
RunRoot):/run/user/1100/containers(including/run/user/1100/containers/networks/aardvark-dns/). - Quadlet unit directory:
/home/docker-data/.config/containers/systemd/(containssymlinkspointingto/data/docker/<service>/*.{container,network}).
/data/docker/<service>.container/2.3 The Four Intra-Service Networking ArchitecturePatterns on ul: Why Network=bridge Is Used
EveryAll container servicetraffic on ul bindsfollows itsfour publishedclean, portdeterministic topatterns awithout dedicatedshared mega-bridges like the retired infra_net:
Ingress (Nginx $\longrightarrow$ Container ULA IPv6 addressSocket):
on eth77 (fd77:7777:5010:0:1337:f1ac:d00d:<hex>/64), and hostHost Nginx (UID 33 http) reverse-terminates public HTTPS (*.drheck.dev) and proxies requestsupstream HTTP/WebSocket traffic to http://[fd77:...:<hex>]:<port>.
All rootless Quadlet definitions on ul specify Network=bridge rather than default pasta networking for two architectural reasons:
pastaeth77/64 ULA IPv6 eth77:
$$ \text{Client HTTPS} \longrightarrow \text{Host Nginx } (\texttt{UID 33}) \longrightarrow [\texttt{fd77:7777:5010:0:1337:f1ac:d00d:hex}]:P_{\text{host}} \longrightarrow \text{Rootless Container } (\texttt{UID 1100}) $$
Container $\longrightarrow$ Native Host Daemons (PostgreSQL, Valkey, Ollama) via 169.254.1.2:
Native postgresql.service (127.0.0.1:5432), valkey.service (127.0.0.1:6379, per-service ACL users), and ollama.service (127.0.0.1:11434) listen exclusively on host loopback. Inside any rootless container, pasta translates 169.254.1.2 directly to host 127.0.0.1:
$$ \text{Container App} \longrightarrow \texttt{169.254.1.2}:(5432, 6379, 11434) \stackrel{\texttt{pasta}}{\longrightarrow} \text{Host } \texttt{127.0.0.1}:(5432, 6379, 11434) $$
Container $\longrightarrow$ Host OIDC / WOPI / LDAPS Hairpin (AddHost=auth.drheck.dev:169.254.1.2):
Inside a rootless pasta network namespace, connecting to an IP address owned by the host intoitself (10.0.0.100, 10.0.0.3, or a host fd77:... ULA address) loops back inside the container namespace.namespace When(Connection refused) rather than hairpinning out to host Nginx connectsor fromHAProxy. theEvery sameOIDC, machineWOPI, or LDAPS client container therefore declares:
AddHost=auth.drheck.dev:169.254.1.2
(plus AddHost=cloud.drheck.dev:169.254.1.2 and AddHost=office.drheck.dev:169.254.1.2 on Nextcloud + Collabora). This preserves full Let's Encrypt TLS SNI and hostname verification while routing cleanly over loopback to host Nginx () src127.0.0.1:443=and fd77:HAProxy (127.0.0.1:636).
Multi-Container Private Stack Bridges (<service>. + .networkaardvark-dns) & Privileged Port Proxy (HAProxy):<hex>
authentik, dst = fd77:...:<hex>pastarootlessporttldrawimmich, sopropenpot, demucsnextcloud+collabora, drawpilebookstack, navidromeromm, vaultwardenyourls, wipstudio, webshell+step-ca) 0.0.0.0:<port>[::]:<port>10.89.X.0/24)Network=bridge/usr/lib/podman/rootlessportaardvark-dns. bookstack_db, romm-db, yourls-db, penpot-postgres, penpot-redis) publish zero host UID connections1100 does not bind privileged ports < 1024, authentik-ldap listens on [fd77:...:<hex>]aaaa]:<host_port>3389 (LDAP) and netavark:6636 10.88.0.x:<container_port>/etc/haproxy/conf.d/ldap.cfg)$$zero-copy \text{NginxLayer-4 }TCP (\texttt{http})passthrough \stackrel{\text{IPv6from TCP}}{\longrightarrow}127.0.0.1, 10.0.0.3, 10.0.0.100, and [ ports \texttt{fd77:...:hex}]aaaa]389/636 to [fd77:...:/P_{\text{host}}aaaa]:33896636.
3. Quadlet Service Architecture & Migrated Inventory
3.1 How Quadlets Work
Instead of docker-compose.yml, each service directory /data/docker/<service>/ contains a declarative <service>.container file symlinked into /home/docker-data/.config/containers/systemd/<service>.container.
Whenever sudo systemctl --user -M docker-data@ daemon-reload runs (or on boot), /usr/lib/systemd/user-generators/podman-system-generator compiles each .container file into a transient systemd unit at /run/user/1100/systemd/generator/<service>.service.
3.2 MigratedComplete Rootless Podman Service Inventory
(30 Services / 40 Containers)
| Service | Systemd docker-data@) |
|
|---|
anchor
anchor.service
anchor_net
[fd77:...:c4a1]:3000 -> 3000
Host PG (169.254.1.2:5432), OIDC
audiomass
audiomass.service
docker.io/azrikahar/audiomass:latestpodman
[fd77:7777:5010:0:1337:f1ac:d00d:a0d1...:a0d1]:9004 -> 80
Stateless WebAudio editor
1100authentik
authentik-server.serviceauthentik-worker.serviceauthentik-ldap.service
authentik_net
[fd77:...:aaaa]:9000,9443(internal worker)
[fd77:...:aaaa]:3389,6636 (via HAProxy 624388:389,:636db 0), zero host TLS key mounts
bentopdf
bentopdf.service
ghcr.io/alam00000/bentopdf:latestpodman
[fd77:7777:5010:0:1337:f1ac:d00d:bdf...:bdf]:8080 -> 8080
Stateless PDF toolkit
624388bookstack
bookstack.servicebookstack_db.service
bookstack_net
[fd77:...:b00c]:6875 -> 80(internal MariaDB 3306) MariaDB sidecar + OIDC
cam
cam.service
cam_net
[fd77:...:ca30]:8000 -> 8000, 127.0.0.1:8089 -> 8000
/proc/net/arp:ro, YOLOv8n + Reolink Baichuan/RTSP
collabora
collabora.service
nextcloud_net
[fd77:...:7a31]:9980 -> 9980
Strict cloud\.drheck\.dev WOPI allowlist
degoog
degoog.service
degoog_net
[fd77:...:de90]:4444 -> 4444
Host Valkey (db 4), updated via 101/usr/local/bin/degoog-update
demucs
demucs.service
podman
[fd77:...:de3]:8000 -> 8000
ROCm GPU (/dev/dri, /dev/kfd, GroupAdd=keep-groups)
drawpile
drawpile.service
podman
[fd77:.env..:d7a0]:27750-27752UserNS=keep-id, User=1100:1100, SQLite sessions
excalidraw
excalidraw.service
docker.io/excalidraw/excalidraw:latestpodman
[fd77:7777:5010:0:1337:f1ac:d00d:e8ca...:e8ca]:9003 -> 80
Stateless whiteboard
1100funkwhale
funkwhale.service
funkwhale_net
[fd77:...:f7a0]:8085 -> 80
Embedded PG/Redis + LDAPS (624388auth.drheck.dev:636 169.254.1.2)
grill
grill.service
grill_net
[fd77:...:fb02]:8000, 10.0.0.3:8000,8088 -> 8000
SQLite + ESP32-C3 HTTP ingest
immich
immich_server.serviceimmich_machine_learning.service
immich_net
[fd77:...:2283]:2283 -> 2283(internal ML worker) Host PG (
pgvecto.rs) + Host Valkey (db 1) + ROCm GPU
navidrome
navidrome.service
podman
[fd77:...:5a70]:4533 -> 4533
/data/music/flac:ro, SQLite
nextcloud
nextcloud.service
nextcloud_net
[fd77:...:7a31]:8081 -> 80
Host PG (nextcloud_db) + Host Valkey (db 2) + OIDC + Collabora
penpot
penpot-frontend.servicepenpot-backend.servicepenpot-exporter.servicepenpot-postgres.servicepenpot-redis.service
penpot_net
[fd77:...:be49]:9001 -> 8080(4 internal services) ZFS
/data/docker/penpot/{postgres_data,assets} + OIDC
romm
romm.serviceromm-db.service
romm_net
[fd77:...:7033]:8080 -> 8080(internal MariaDB 3306) MariaDB 11 sidecar +
/data/games/roms + OIDC
scontrol
scontrol.service
scontrol_net
[fd77:...:5c01]:8000 -> 8000
id_scontrol SSH orchestration to studio-kvm-ssh
sopro
sopro.service
podman
[fd77:...:509]:7860 -> 7860
Local TTS image (localhost/sopro:latest)
step-ca
step-ca.service
webshell_net
[fd77:...:ca]:9000 -> 9000
Smallstep SSH & X.509 CA (used by webshell & ipctl)
subocr
subocr.service
subocr_net
[fd77:...:c70]:8000 -> 8000, 127.0.0.1:8077 -> 8000
ROCm MIGraphX GPU + Host Ollama (169.254.1.2:11434)
tldraw
tldraw.service
docker.io/ratneo/tldraw:latestpodman
[fd77:7777:5010:0:1337:f1ac:d00d:71d7...:71d7]:9002 -> 80
1100Stateless 624388wickvaultwarden
wick.vaultwarden.service
docker.io/library/nginx:alpinepodman
[fd77:7777:5010:0:1337:f1ac:d00d:41c89005...:a117]:8222 -> 80
1100SQLite + OIDC (624388AddHost=auth.drheck.dev:169.254.1.2vikunja
/data/docker/wick/html:/usr/share/nginx/html:rovikunja.service
vikunja_net
[fd77:...:71c0]:3456 -> 3456
Host PG + Host Valkey (db 3) + OIDC; JSON HealthCmd
webrcade
webrcade.service
docker.io/webrcade/webrcade:latestpodman
[fd77:7777:5010:0:1337:f1ac:d00d:a1ca...:a1ca]:8080 -> 801100624320/data/docker/webrcade/content, /data/games:ro
drawpilewebshell
drawpile.webshell.service
docker.io/drawpile/drawpile-srv:2webshell_net
[fd77:7777:5010:0:1337:f1ac:d00d:d7a0...:771d]:7681 -> 7681
Dual-stack ttyd + ephemeral 5m step-ca SSH certs
wick
wick.service
27750-27752 -> 27750-27752podman
1100[fd77:...:41c8]:9005 1100UserNS=keep-id80
Static HTML5 studio on /data/docker/drawpile/sessions:/data/sessionsnginx:alpine
navidromewipstudio
navidrome.wipstudio.servicewipstudio-minio.service
docker.io/deluan/navidrome:latestwipstudio_net
[fd77:7777:5010:0:1337:f1ac:d00d:5a70...:7190]:3000 -> 3000[fd77:...:7190]:9000 -> 9000
4533Host ->PG 4533+ 1100MinIO 0/data/docker/navidrome/data:/data/data/music/flac:/music:rovaultwardenvaultwarden.servicedocker.io/vaultwarden/server:latestfd77:7777:5010:0:1337:f1ac:d00d:a1178222 -> 8011000/data/docker/vaultwarden/vw-data:/dataOIDC (.envDISABLE_SIGN_UP=true)
soproyourls
sopro.yourls.serviceyourls-db.service
localhost/sopro:latestyourls_net
[fd77:7777:5010:0:1337:f1ac:d00d:5097860...:77]:8080 -> 78608080(internal MariaDB 3306)
1100MariaDB 10.11 sidecar (0YOURLS_PRIVATE=true/data/docker/sopro/cache:/root/.cache/huggingfacedemucsdemucs.servicelocalhost/demucs:latestfd77:7777:5010:0:1337:f1ac:d00d:de38000 -> 800011000keep-groupsForwardAuth /data/docker/demucs/{app.py,static,cache,media}/dev/dri/dev/kfdAPI)
3.3 Reference Quadlet PatternsExamples
Pattern A: StandardOIDC Stateful+ Host PostgreSQL/Valkey Service (e.g., /data/docker/vaultwarden/vaultwarden.vikunja/vikunja.container)
[Unit]
Description=VaultwardenVikunja Task Manager (Rootless Podman ContainerPodman)
After=network-online.target
Wants=network-online.target
[Container]
Pull=never
ContainerName=vaultwardenvikunja
Image=docker.io/vaultwarden/server:vikunja/vikunja:latest
Network=bridgevikunja.network
User=0:0AddHost=auth.drheck.dev:169.254.1.2
PublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:a117]71c0]:8222:803456:3456
EnvironmentFile=/data/docker/vaultwarden/vikunja/.env
Environment=TZ=America/New_York
Volume=/data/docker/vaultwarden/vw-data:vikunja/config.yml:/dataetc/vikunja/config.yml:ro
Volume=/data/docker/vikunja/files:/app/vikunja/files:rw
HealthCmd=["/app/vikunja/vikunja", "healthcheck"]
HealthInterval=30s
HealthRetries=3
[Service]
Restart=always
RestartSec=5
TimeoutStartSec=120
[Install]
WantedBy=default.target
Because container 0:0 maps directly to host docker-data:docker-data (1100:1100), files written inside /data are owned on the host ZFS dataset by docker-data:docker-data (1100:1100).
Pattern B: Non-RootMulti-Container EnforcedROCm BinaryGPU with UserNS=keep-idStack (e.g., /data/docker/drawpile/drawpile.immich/immich_machine_learning.container)
[Unit]
Description=DrawpileImmich ROCm Machine Learning Worker (Rootless Podman ContainerPodman)
After=network-online.target
Wants=network-online.target
[Container]
Pull=never
ContainerName=drawpileimmich_machine_learning
Image=docker.ghcr.io/drawpile/drawpile-srv:2immich-app/immich-machine-learning:v3.3.1-rocm
Network=bridge
UserNS=keep-id
User=1100:1100
Exec=--port 27750 --websocket-port 27751 --web-admin-port 27752 --web-admin-access all --web-admin-auth admin:drawpile123 --sessions /data/sessions --database /data/sessions/drawpile.db
Volume=/data/docker/drawpile/sessions:/data/sessions
PublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:d7a0]:27750:27750
PublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:d7a0]:27751:27751
PublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:d7a0]:27752:27752
[Service]
Restart=always
[Install]
WantedBy=default.target
drawpile-srv aborts on startup if internal getuid() == 0. Setting UserNS=keep-id and User=1100:1100 makes the process see UID 1100 inside the container while still mapping to UID 1100 (docker-data) on the host filesystem.
Pattern C: AMD ROCm GPU Passthrough with GroupAdd=keep-groups (e.g., /data/docker/demucs/demucs.container)
[Unit]
Description=Demucs ROCm Rootless Podman Container
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=demucs
Image=localhost/demucs:latest
Network=bridgeimmich.network
AddDevice=/dev/dri:/dev/dri
AddDevice=/dev/kfd:/dev/kfd
GroupAdd=keep-groups
PublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:de3]:8000:8000
EnvironmentFile=/data/docker/demucs/immich/.env
Environment=TZ=America/New_York
Environment=TORCH_HOME=/root/.cache/torch
Environment=HF_HOME=/root/.cache/huggingface
Environment=MIOPEN_USER_DB_PATH=/root/.cache/miopen
Environment=MIOPEN_CUSTOM_CACHE_DIR=/root/.cache/miopen
Volume=/data/docker/demucs/app.py:immich/model-cache:/app/app.py:ro
Volume=/data/docker/demucs/static:/app/static:ro
Volume=/data/docker/demucs/cache/torch:/root/.cache/torch
Volume=/data/docker/demucs/cache/huggingface:/root/.cache/huggingface
Volume=/data/docker/demucs/cache/miopen:/root/.cache/miopen
Volume=/data/docker/demucs/media:/app/mediacache:rw
[Service]
Restart=always
RestartSec=5
TimeoutStartSec=180
[Install]
WantedBy=default.target
GroupAdd=keep-groups instructs crun not to call setgroups(0, NULL) when entering the user namespace, preserving docker-data's host membership in video (91) and render (991) so /dev/kfd and /dev/dri/renderD128 are accessible to PyTorch ROCm.
4. Day-to-Day Operations: Status, Logs & Lifecycle
Important Execution Context:Context on Arch Linux: Rootless Podman containers belongrun tounder docker-data (UID 1100), not root or luke. RunningTwo bareArch Linux systemd/podman details matter in CLI commands:
1. Always run sudo podman-u ps inspects root's empty store, not docker-data's store.from Alwaysa useworld-traversable directory such as (otherwise sudocd /tmppodman tries to stat("/home/luke") on mode 0700 and warns about Permission denied).
2. On systemd 258+, systemctl --user -M docker-data@ ... forworks servicedirectly, management, or setwhereas returns HOME=/home/journalctl --user -M docker-data@Connecting to a machine as non-root is not supported. Always read user unit journals via sudo -u docker-data XDG_RUNTIME_DIR=/run/user/1100 journalctl --user -u <service>.service from a directory readable by docker-data (such as cd /home/docker-data) when invoking podman directly..
4.1 Checking ServiceService, Container & ContainerNamespace Status
# 1. Host-wide health summary across all nginx, native systemd, and rootless Podman services
$ status_all
$ services
# 2. Live container-to-host UID, PID, and UserNS translation audit tree
$ sudo /usr/local/sbin/podman-map-tree
# 3. Check systemd unit status for a specificany Quadlet servicecontainer
$ sudo systemctl --user -M docker-data@ status vaultwarden.authentik-server.service
# 2. List all active Quadlet container units under docker-data
$ sudo systemctl --user -M docker-data@ list-units --type=service --state=running
# 3.4. List all running rootless Podman containers, networks, ports, and uptimehealth states
$ (cd /home/docker-datatmp && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman ps -a)
# 4.5. View liveLive CPU, memory, and I/O resource usagetelemetry across all rootless40 containers
$ (cd /home/docker-datatmp && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman stats --no-stream)
# 5. Inspect processes running inside a container (showing container UID vs host UID)
$ (cd /home/docker-data && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman top vaultwarden user uid huser huid pid comm)
4.2 Viewing Service & Container Logs
Because Quadlet services integrate directly with systemd (--sdnotify=conmon), container output is available through both journalctl and podman logs:
# Follow live systemd journaluser unit logs for a service (includes systemd start/stop eventslifecycle + container stdout/stderr)
$ sudo -u docker-data XDG_RUNTIME_DIR=/run/user/1100 journalctl --user -M docker-data@ -u navidrome.authentik-server.service -f
# View the last 100 journal lines ofwithout systemd journal logspaging
$ sudo -u docker-data XDG_RUNTIME_DIR=/run/user/1100 journalctl --user -M docker-data@ -u demucs.immich_server.service -n 100 --no-pager
# ViewFollow container stdout/stderr directly via podman logs
$ (cd /home/docker-datatmp && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman logs --tail 100 -f sopro)bookstack)
4.3 Starting, Stopping, Restarting & EditingApplying QuadletsQuadlet Edits
# Restart a servicerunning Quadlet container
$ sudo systemctl --user -M docker-data@ restart vaultwarden.service
# StopReload orsystemd startgenerator aafter service
$ sudo systemctl --user -M docker-data@ stop drawpile.service
$ sudo systemctl --user -M docker-data@ start drawpile.service
# Apply edits made toediting /data/docker/<service>/<service>.container
$ sudo systemctl --user -M docker-data@ daemon-reload
$ sudo systemctl --user -M docker-data@ restart <service>.service
4.4 Updating Images & Rebuilding Local Dockerfiles
Because
vaultwardennavidrome.containerbentopdfPull=never# Pull updated registry image into docker-data's store and restart
$ (cd /home/docker-datatmp && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman pull docker.io/vaultwarden/server:latest)
$ sudo systemctl --user -M docker-data@ restart vaultwarden.service
# built Dockerfile soprodemucsTMPDIR=/dev/shm demucs$ (cd /data/docker/demucssubocr && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 TMPDIR=/dev/shm podman build -t localhost/demucs:subocr:latest /data/docker/demucs)subocr)
$ sudo systemctl --user -M docker-data@ restart demucs.subocr.service
$ (cd /home/docker-data && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman image prune -f)
4.5 Onboarding a New Standalone Service
eth77fd77:7777:5010:0:1337:f1ac:d00d:<hex>/64/etc/systemd/network/sudo ip -6 addr add ... dev eth77/data/docker/<service>/<service>.containerNetwork=bridgePublishPort=[fd77:7777:5010:0:1337:f1ac:d00d:<hex>]:<host_port>:<container_port>$ sudo chown -R docker-data:docker-data /data/docker/<service>
$ sudo chmod 755 /data/docker/<service>
$ sudo -u docker-data ln -sf /data/docker/<service>/<service>.container /home/docker-data/.config/containers/systemd/<service>.container
$ sudo systemctl --user -M docker-data@ daemon-reload
$ sudo systemctl --user -M docker-data@ start <service>.service
/home/luke/git/backups/ul/scripts/backup/<service>/home/luke/git/backups/ul/scripts/restore/<service><service>.containershellcheck5. Troubleshooting & Diagnostics Runbook
5.1 Symptom: LocalOIDC curlLogin or NginxContainer TimesLDAPS OutFails / Resets onwith [fd77:...]:PORTConnection refused to auth.drheck.dev
- Root Cause:
IfaNetwork=pasta(the default whenNetwork=is omitted) is used without isolated-aaddresses,rootlesspastacopiesnetworkallnamespace,36auth.drheck.devresolves via DNS to hostULA10.0.0.100IPv6 addresses fromor,eth77fd77:...:aaaaintowhichthe container namespace, causing replies to local connections from the host's own ULA IPv6 address to looploops back inside thecontainer.
Network=pasta:-a,10.0.2.100,-a,fd00::100pastawhere 0.0.0.0:<port>:443tldrawsoprodemucsdrawpile:636RSTcurl: (56) Recv failure: Connection reset by peer.containerAddHost=auth.drheck.dev:169.254.1.2 Network=bridge[Container]sudo systemctl --user -M docker-data@ daemon-reload && sudo systemctl --user -M docker-data@ restart <service>.serviceNetwork=bridgecontainer, reload rootlessportdaemon-reload, 10.88.--map-host-loopback to 127.0.x0.1:443127.0.0.1:636.
5.2 Symptom: Multi-Container Service Fails to StartConnect withto Sibling Hostname (penpot-redis / bookstack_db) After Abrupt Kill
<service>.network bridge is killed abruptly without running netavark teardown, a stale container ID + dead IP line can remain ahead of the new container's IP in /run/user/1100/containers/networks/aardvark-dns/<net_name>. Clients that connect only to the first A record returned by DNS (such as Java's Lettuce client in penpot-backend) time out against the stale IP.
Diagnosis & Fix:
/run/user/1100/containers/networks/aardvark-dns/<net_name> against live container IDs from podman ps --no-trunc.
Remove any line whose 64-character container ID is no longer running, then reload aardvark-dns without restarting healthy containers:
$ sudo kill -HUP "$(cat /run/user/1100/containers/networks/aardvark-dns/aardvark.pid)"
5.3 Symptom: Permission denied onInside VolumesNon-Root orWorker RefusesContainer (UID 0624320 / 625288) on /data/docker/<svc>/...
CaseRoot1Cause:—WorkerApplicationprocessesrefuses to runrunning asroot(getuid() == 0) inside thecontainer(e.g.,drawpile-srv): Under default rootless Podman,User=0:0maps toUID$u > 0$ execute on the1100host, so the process is unprivileged on the host—but inside the containergetuid()returns0. If a binary explicitly checksgetuid() == 0and exits, add bothUserNS=keep-idandUser=1100:1100to[Container].
nginxapache2root0 -> 1100101$624287 + u$ (624388336243206243886243201100)other/data/docker/<servicesvc> has mode chmod 7552770 (drwxr-xr-drwxrws---, missing other execute +x)html, contentchmod -R a+rXpodman unsharedocker-data/data/docker/<svc> to $host (cd /home/docker-data && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman unshare ls -lan /data/docker/vaultwarden/vw-data)
5.3 Symptom:like Error:git chdir("/home/luke"pull): Permissioncan deniedaccidentally When Runningwrite sudoroot-owned -ufiles docker-data podman
sudo625287-owned /home/luke0700 luke:lukepodmanstat()chdir()Permission deniedsudo chmod 2771 /data/docker/<svc>, i.e., /home/docker-datadrwxrws--x).
Execute inside-container file updates inside the container namespace (or via podman unshare), just as /tmpusr/local/bin/degoog-updateHOMEgit fetch and XDG_RUNTIME_DIRbun installinside $degoog.
5.4 Symptom: QuadletDistroless Container Marks .containerunhealthy FileDespite EditsListening NotNormally Applied(e.g., or Service Not Foundvikunja)
DiagnosticRootCommandCause:RunDistrolesstheimagesQuadletcontainsystemdnogenerator/bin/sh, so string-formHealthCmd=/app/vikunja/vikunja healthcheckfails because Podman invokes/bin/sh -cunlessHealthCmdis formatted as a JSON exec array.
HealthCmd=["/app/vikunja/vikunja", "healthcheck"] as a JSON array in --dry-rundocker-data/home/docker-data/.config/containers/systemd/*<service>.container.
5.5 Symptom: 502 Bad Gateway on Dual-Stack Service When Passing IPv6-Only Flag (e.g., ttyd -6 -i ::)
eth77 ULA IPv6 socket. Passing IPv6-only bind flags inside the container rejects IPv4 loopback/bridge forwards.
Fix: Let internal container daemons listen on default dual-stack (0.0.0.0 / ::) rather than forcing IPv6-only flags.
5.6 Validating Quadlet Syntax Before Restarting
$ (cd /home/docker-datatmp && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 /usr/lib/systemd/user-generators/podman-system-generator --user --dry-run)
/home/docker-data/.config/containers/systemd/<service>.container/data/docker/<service>/<service>.container/data/docker/<service>docker-datasudo systemctl --user -M docker-data@ daemon-reloadstart-limit-hit$ sudo systemctl --user -M docker-data@ reset-failed <service>.service
$ sudo systemctl --user -M docker-data@ restart <service>.service
5.5 Symptom: ROCm GPU (torch.cuda.is_available() == False) Inside Rootless Container
docker-datavideo91render991id docker-data.container[Container]AddDevice=/dev/dri:/dev/dri
AddDevice=/dev/kfd:/dev/kfd
GroupAdd=keep-groups
demucs$ (cd /home/docker-data && sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman exec demucs python3 -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))")
True AMD Radeon AI PRO R9700
5.6 Full Audit Script: Verify Host UIDs, User Namespaces & HTTP Health
Run the following one-liner on ul to audit every rootless Podman service's host kernel UID, user namespace isolation, and ULA IPv6 HTTP response:
$ (cd /home/docker-data && init_ns=$(sudo readlink /proc/1/ns/user) && echo "Init UserNS: $init_ns" && \
for c in audiomass bentopdf excalidraw tldraw wick webrcade drawpile navidrome vaultwarden sopro demucs; do \
pid=$(sudo -u docker-data HOME=/home/docker-data XDG_RUNTIME_DIR=/run/user/1100 podman inspect "$c" --format "{{.State.Pid}}"); \
uid=$(awk "/^Uid:/ {print \$2}" "/proc/$pid/status"); \
ns=$(sudo readlink "/proc/$pid/ns/user"); \
printf "%-12s PID=%-8s HostUID=%-8s UserNS=%s\n" "$c" "$pid" "$uid" "$ns"; \
done)