CertGrid CertGrid
Concepts·Podman

Podman Without a Daemon

Start a container, then look for the process that owns it. `pgrep podman` finds nothing, `podman.service` is inactive, and the running nginx is a child of PID 1 by way of a 96-argument monitor called conmon.

Foundations Guide 2 of 47 Beginner

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.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. Start something long-running

    A container that exits immediately proves nothing about what supervises it, so start nginx and leave it running.

    Then ask the obvious question: which process is Podman?

    bash Example session
    podman run -d --name web docker.io/library/nginx:alpine55f03c179ef1648c4a1fa40440f177a9ef06ff3274cc37e05e77d9bb7c755decpgrep -a podman [exit 1]

    Expected resultA container ID, then pgrep -a podman printing nothing and exiting 1.

    Success conditionA container is running and there is no podman process on the machine.

  2. One monitor per container, parented to init

    There is exactly one new process, and it is not Podman. It is conmon - the container monitor - and there is one of them per running container.

    Two fields in the ps output carry the whole architecture:

    • PPID is 1. conmon's parent is systemd, not a Podman daemon. Podman double-forks it and exits, so conmon is re-parented to init and survives the command that created it.
    • USER is your own account. Nothing here belongs to root.

    pstree draws it in one line. Read it as the answer to "what would a daemon have been doing":

    systemd(1)---conmon(4859)---nginx(4861)
    bash Example session
    pgrep -c conmon1ps -o pid,ppid,user,comm -p $(pgrep conmon | head -1)    PID    PPID USER     COMMAND   4859       1 sysadmin conmonpstree -sp $(pgrep conmon | head -1)systemd(1)---conmon(4859)---nginx(4861)-+-nginx(4886)                                        `-nginx(4887)

    Expected resultOne conmon, PPID 1, owned by you, with nginx as its child.

    Success conditionYou can point at the process that supervises the container, and it is not a daemon.

  3. Why nothing needs to stay running

    The obvious objection: if podman has exited, what cleans up when the container stops? Who removes the network, unmounts the layers, writes the exit code?

    conmon does, and it was told how at creation time. Its command line is 96 arguments long, and two of them are the answer:

    • -r /usr/bin/crun - the runtime it drives
    • --exit-command /usr/bin/podman - the command to run when the container dies

    That is the trick. Everything a daemon would have remembered is instead passed to the monitor once, up front. There is no shared state to keep in memory, so there is nothing that has to stay running to hold it. The cost is 96 arguments; the benefit is that killing any one container's monitor cannot affect any other container, and there is no single process whose crash takes down everything.

    Meanwhile systemd still reports no Podman service, while podman ps answers perfectly well - because podman ps reads a database on disk, not a daemon.

    bash Example session
    ps -o args= -p $(pgrep conmon | head -1) | tr ' ' '\n' | grep -c .96ps -o args= -p $(pgrep conmon | head -1) | grep -oE '\-r /usr/bin/[a-z]+|--exit-command /usr/bin/[a-z]+'-r /usr/bin/crun--exit-command /usr/bin/podmansystemctl is-active podman.serviceinactive[exit 3]

    Expected result96 arguments, the crun runtime and the podman exit command, and inactive from systemd.

    Success conditionYou can explain what replaces the daemon's memory, and it is a list of arguments.

  4. Stop it, and watch the monitor go

    The lifecycle is symmetric. Stopping the container ends its monitor, and the count returns to zero. No service was started and none is left behind.

    This is worth internalising before the Systemd and Quadlet track, because it explains what that track is for: with no daemon to restart your containers at boot, systemd is the thing that does it - not as a Podman service, but as one unit per container.

    bash Example session
    podman stop webwebpgrep -c conmon1podman rm webweb

    Expected resultThe container name echoed back, then 0 conmon processes with exit 1.

    Success conditionNo container, no monitor, and nothing running that has to be cleaned up.

Troubleshooting

Official sources