Podman Pod Restart with systemd
A `.pod`, a `.volume` and a `.container` file, and starting one of them starts all three. Then `systemctl restart` on the container unit fails with `Bound to unit app-pod.service, but unit isn't active` - and takes the pod down with it.
systemd and Quadlet Guide 24 of 47 Advanced
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 15 min
- Reviewed22 August 2026
Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
-
Three file types, one application
Quadlet has a file type per Podman object. Three of them here:
appdata.volumewith[Volume] VolumeName=appdata- a named volumeapp.podwith[Pod] PodName=appandPublishPort=8091:80- note the port is on the pod, exactly as guide 21 showed it must beapp-web.containerwhich joins them withPod=app.podandVolume=appdata.volume:/usr/share/nginx/html:z
The references are filenames, not object names.
Pod=app.podnames the file, and the generator turns that into a dependency on the unit that file produces. That is what lets it order things correctly without you writingAfter=anywhere.Setting
VolumeName=andPodName=explicitly is worth the two lines. Without them Quadlet prefixes the object withsystemd-, soappdata.volumewould create a volume calledsystemd-appdata- which is findable but surprising the first time you go looking for it.bash Example session cat > ~/.config/containers/systemd/app.pod <<'UNIT'[Pod]PodName=appPublishPort=8091:80UNITcat > ~/.config/containers/systemd/app-web.container <<'UNIT'[Unit]Description=web, inside the app pod [Container]Image=docker.io/library/nginx:alpineContainerName=app-webPod=app.podVolume=appdata.volume:/usr/share/nginx/html:z [Install]WantedBy=default.targetUNITls ~/.config/containers/systemd/app-web.containerapp.podappdata.volumemyapp.containerweb.containerExpected resultFour unit files in the directory, including the ones from earlier guides.
Success conditionA pod, a volume and a container are each described by their own file.
-
One start brings up three units
daemon-reload, then start only the container unit.is-activeon both reportsactivefor the pod service as well - you did not start it.podman pod psshows the pod running with two containers, andpodman volume lsshowsappdatacreated.The generator wired all of it. Look at what it put in
After=:app-pod.service appdata-volume.service app.slice basic.targetThe pod and the volume both have to exist before the container can join them, and systemd was told so. This is the argument for Quadlet over a shell script: dependency ordering is a solved problem in systemd, and expressing containers as units gets you the solution rather than a sequence of commands with
sleepin it.bash Example session systemctl --user daemon-reloadsystemctl --user list-unit-files 'app*'UNIT FILE STATE PRESETapp-pod.service generated -app-snap\x2duserd\x2dautostart@autostart.service generated -app-web.service generated -appdata-volume.service generated -app.slice static - 5 unit files listed.systemctl --user start app-web.servicesystemctl --user is-active app-pod.service app-web.serviceactiveactivepodman pod psPOD ID NAME STATUS CREATED INFRA ID # OF CONTAINERSd51b5c5e7c31 app Running Less than a second ago 564a67815f03 2podman volume lsDRIVER VOLUME NAMElocal 28510849432f70bcfae53e60894030b8b8fe5ffe4bee9ba10dc4ff2ba504367alocal appdataExpected resultThree generated units, two active services, a running pod and an
appdatavolume.Success conditionStarting one unit produced a pod, a volume and a container.
-
The volume is a real volume
Write a file through the container into the mounted volume, and
curlthe published port - which is on the pod, not the container.served from a quadlet volumecomes back, andpodman volume inspectshows the data living at~/.local/share/containers/storage/volumes/appdata/_data- inside your own rootless store, as guide 14 would predict.The
:zsuffix on theVolume=line is an SELinux relabel. It does nothing on Ubuntu, which uses AppArmor, and it is harmless - worth leaving in if the unit might ever run on RHEL or Fedora, where without it the container cannot read the volume at all.bash Example session podman exec app-web sh -c 'echo "served from a quadlet volume" > /usr/share/nginx/html/index.html'curl -s http://localhost:8091served from a quadlet volumepodman volume inspect appdata --format 'mount={{.Mountpoint}}'mount=/home/sysadmin/.local/share/containers/storage/volumes/appdata/_dataExpected resultThe written line served over HTTP, and a mountpoint in your rootless store.
Success conditionData written inside the container is served through the pod's port.
-
Now restart it the obvious way, and watch it break
The instinct is
systemctl --user restart app-web.service. It fails:A dependency job for app-web.service failed. See 'journalctl -xe' for details.And it is worse than a failed command -
curlnow exits 7 andpodman psshows no app containers at all. The pod is gone. One restart took down the whole application.The journal names the cause exactly:
app-web.service: Bound to unit app-pod.service, but unit isn't active. Dependency failed for app-web.serviceBindsTo=app-pod.service. Stopping the container triggers the pod unit's own stop, which removes the pod - and by the time systemd tries to start the container again, the pod it is bound to is mid-teardown and not active. The start is refused, and nothing brings the pod back because the thing that would have is the job that just failed.This is a genuine sharp edge, not a misconfiguration. It follows from a pod being a container's container: there is no meaningful way to restart one member of a pod whose lifecycle owns it.
bash Example session systemctl --user restart app-web.serviceA dependency job for app-web.service failed. See 'journalctl -xe' for details.[exit 1]curl -s http://localhost:8091served from a quadlet volumepodman ps --format 'table {{.Names}} {{.Status}}'NAMES STATUSquadlet-web Up 4 minutesregistry Up About a minutemyapp Up 56 secondsExpected resultA dependency failure, a refused connection, and no app containers running.
Success conditionYou have reproduced the failure and can name the directive that causes it.
-
Restart the pod instead
Confirm the wiring first -
BindsToreally isapp-pod.service- then restart that:$ systemctl --user restart app-pod.service $ systemctl --user is-active app-pod.service app-web.service active activeBoth back. The pod is recreated, the container rejoins it, and
curlreturnsserved from a quadlet volume- the data survived, because it was in a volume and the volume unit was never part of the restart.So the operating rule for a Quadlet pod:
systemctl restartto cycle the application-pod.service systemctl startto bring one member up when the pod is already running-web.service - never
restarta container unit that hasPod=
And the general lesson, which is the one worth carrying past this guide: Quadlet units are ordinary systemd units, so when one behaves strangely,
systemctl showthe dependency properties and read the journal. The answer was oneBindsToline, and no amount of reasoning about Podman would have found it.bash Example session systemctl --user show app-web.service --property=BindsTo --valueapp-pod.servicesystemctl --user restart app-pod.servicesystemctl --user is-active app-pod.service app-web.serviceactiveactivepodman ps --format 'table {{.Names}} {{.Status}}'NAMES STATUSquadlet-web Up 5 minutesregistry Up About a minutemyapp Up About a minuteapp-infra Up 1 secondapp-web Up 1 secondcurl -s http://localhost:8091served from a quadlet volumeExpected result
BindsTo=app-pod.service, both units active again, and the volume's content still served.Success conditionThe application is back and its data came with it.
Troubleshooting
Bound to unit <x>-pod.service, but unit isn't active.Why: A container unit with
Pod=was restarted directly.Fix:
systemctl --user restart <x>-pod.service. If it is already broken, starting the pod unit brings everything back.A volume appears as
systemd-appdatarather than the name you expected.Why: No
VolumeName=in the.volumefile, so Quadlet prefixed it.Fix:Set
VolumeName=explicitly. Renaming later means moving the data, so it is worth doing up front.The container starts before the volume is ready, or complains the pod does not exist.
Why:
Pod=orVolume=names an object rather than a file.Fix:Use the filename -
Pod=app.pod, notPod=app. The generator can only derive a unit dependency from a file it knows about.Removing a
.containerfile leaves its container running.Why:
daemon-reloadstops generating the unit but does not stop what is already running.Fix:Stop the service first, then delete the file, then
daemon-reload. In the other order you are left with a container no unit owns.