The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Inspect engine and host | podman info --format json | View examples |
| Check subordinate ID ranges | getent subuid "$USER"; getent subgid "$USER" | View examples |
| Pull an immutable image | podman pull docker.io/library/nginx@sha256:DIGEST | View examples |
| Run with a read-only root | podman run --read-only --cap-drop=all \
--security-opt=no-new-privileges --tmpfs \
/tmp:rw,noexec,nosuid,nodev IMAGE | View examples |
| Publish only on loopback | podman run -p 127.0.0.1:8080:8080 IMAGE | View examples |
| Bound memory and CPUs | podman run --memory=512m --cpus=1.5 --pids-limit=256 \
IMAGE | View examples |
| Create an application network | podman network create app-net | View examples |
| Create a named volume | podman volume create app-data | View examples |
| Relabel a private bind mount | podman run -v /srv/app/config:/app/config:ro,Z IMAGE | View examples |
| Create a secret from standard input | printf '%s' "$DB_PASSWORD" |
podman secret create db_password - | View examples |
| Mount a runtime secret | podman run --secret db_password,type=mount IMAGE | View examples |
| Provide a build secret | podman build --secret id=npmrc,src="$HOME/.npmrc" -t \
localhost/app:build . | View examples |
| Regenerate rootless units | systemctl --user daemon-reload | View examples |
| Verify generated service | systemd-analyze --user --generators=true verify \
app.service | View examples |
| Inspect health state | podman inspect --format '{{.State.Health.Status}}' app | View examples |
| Stream lifecycle events | podman events --filter container=app | View examples |
| Preview automatic updates | podman auto-update --dry-run | View examples |
| Export a named volume | podman volume export app-data --output app-data.tar | View examples |
| Archive an OCI image | podman save --format oci-archive --output app-image.tar \
IMAGE | View examples |
| Run Podman diagnostics | podman system check --quick | View examples |
Podman is a daemonless OCI container engine whose rootless mode gives each user separate storage, containers, and user-namespace mappings. Production operation still requires explicit image provenance, least privilege, persistent-data ownership, secret handling, health policy, and tested recovery. These recipes favor inspectable changes and fully qualified image names; review them against the installed Podman, kernel, cgroup, SELinux or AppArmor, and distribution versions before rollout.
Step by step
Detailed examples
Establish a rootless, version-aware baseline
Run Podman as the service account that will own the workload. Rootless and rootful stores are separate, so sudo podman does not reveal a user's containers. Normal rootless operation needs subordinate UID and GID ranges; after an administrator changes those ranges, existing mappings and storage may require a deliberate migration. Rootless OverlayFS needs a sufficiently recent kernel or fuse-overlayfs, and NFS-backed home directories require local graph storage. Quadlet requires cgroup v2. Package names, default network helpers, and feature availability vary by distribution and Podman release.
podman version
podman info --format 'rootless={{.Host.Security.Rootless}} cgroups={{.Host.CgroupsVersion}} graph={{.Store.GraphDriverName}}'
getent subuid "$USER"
getent subgid "$USER"
findmnt -T "$HOME" -o TARGET,FSTYPE,OPTIONS
podman system connection list Run containers with least privilege and bounded resources
Container isolation is not a permission to trust the workload. Start rootless, drop capabilities, block privilege escalation, make the root filesystem read-only, and add back only the writable paths and capabilities the process demonstrates it needs. Avoid --privileged, host PID or network namespaces, broad device access, and mounting the Podman socket: each materially expands host control. Pin production images by digest and establish an explicit update process. Low host ports, cgroup delegation, and resource flags differ across kernels and distributions, so verify effective settings with podman inspect and cgroup metrics.
image='docker.io/library/nginx@sha256:DIGEST'
podman run -d --name web --replace \
--read-only --cap-drop=all --security-opt=no-new-privileges \
--tmpfs /var/cache/nginx:rw,nosuid,nodev --tmpfs /var/run:rw,nosuid,nodev \
--memory=512m --cpus=1.5 --pids-limit=256 \
-p 127.0.0.1:8080:8080 "$image"
podman inspect web --format '{{json .HostConfig}}' Make network exposure and data ownership explicit
A published port binds on every host address unless an address is supplied, so bind loopback unless direct network exposure is intentional. Rootless networking commonly uses pasta on current Podman releases, while older or differently configured installations may use slirp4netns; latency, source-address behavior, and host reachability differ. Prefer named volumes for portable engine-managed data. For bind mounts, validate ownership through the user namespace and apply :Z only to content private to one container; relabeling system paths can break host services. Never remove or prune storage until inventory, backups, and restore tests are complete.
podman network create app-net
podman volume create app-data
podman network inspect app-net
podman volume inspect app-data
podman run --rm --network app-net \
--mount type=volume,source=app-data,target=/data \
docker.io/library/alpine@sha256:DIGEST sh -c 'id; stat -c "%u:%g %a %n" /data' Keep credentials out of images, arguments, and logs
Environment variables, Containerfile ARG values, command-line arguments, and image layers are routinely exposed through inspection, process listings, caches, or logs. Use Podman secrets as mounted files for runtime credentials and build-secret mounts for package-manager tokens. A secret is scoped to the Podman store and is not automatically backed up with a container. Rotate it through the authoritative secret manager, recreate or restart consumers as required by the application, and avoid printing its content. Prefer registries with TLS verification and authenticate through a protected auth file rather than embedding credentials in image names or scripts.
read -r -s -p 'Database password: ' DB_PASSWORD
printf '\n'
printf '%s' "$DB_PASSWORD" | podman secret create db_password -
unset DB_PASSWORD
podman secret inspect db_password
podman run --rm --secret db_password,type=mount \
docker.io/library/alpine@sha256:DIGEST \
sh -c 'test -s /run/secrets/db_password && echo secret-mounted' test -r "$HOME/.npmrc"
podman build \
--secret id=npmrc,src="$HOME/.npmrc" \
--label org.opencontainers.image.revision=GIT_COMMIT \
-t localhost/app:build .
podman image inspect localhost/app:build --format '{{json .Config.Labels}}' Manage long-running containers declaratively with Quadlet
Quadlet turns .container, .volume, .network, and related definitions into ordinary systemd services. Rootless definitions belong under ~/.config/containers/systemd and are controlled with systemctl --user; setting User= in a rootful Quadlet does not turn it into a rootless workload. The generated service is not the source of truth and must not be edited or enabled directly. Put the desired target in the Quadlet's [Install] section; the generator applies it during reload. Verify and start the service after reloading. User services that must survive logout require an administrator to enable lingering. Pre-pull images or raise TimeoutStartSec where registry latency can exceed the service startup timeout.
install -d -m 0700 "$HOME/.config/containers/systemd"
install -m 0600 ./app.container "$HOME/.config/containers/systemd/app.container"
systemctl --user daemon-reload
systemd-analyze --user --generators=true verify app.service
systemctl --user start app.service
systemctl --user status app.service --no-pager
journalctl --user -u app.service -n 100 --no-pager [Unit]
Description=Rootless application container
After=network-online.target
Wants=network-online.target
[Container]
Image=registry.example.com/team/app@sha256:DIGEST
ContainerName=app
PublishPort=127.0.0.1:8080:8080
ReadOnly=true
NoNewPrivileges=true
DropCapability=all
Volume=app-data.volume:/var/lib/app
Secret=db_password,type=mount
HealthCmd=/usr/local/bin/healthcheck
HealthInterval=30s
HealthTimeout=5s
[Service]
Restart=on-failure
TimeoutStartSec=300
[Install]
WantedBy=default.target Observe health and stage updates for rollback
A health check should test a meaningful local dependency, finish quickly, and avoid credentials in output. Health status does not by itself restart a standalone container; combine application readiness with the Quadlet and systemd failure policy appropriate to the service. Treat logs and events as potentially sensitive. For automatic updates, use a fully qualified image reference and run --dry-run first. Digest pinning deliberately disables tag-driven movement, so update the reviewed digest through deployment configuration. Registry outages, authentication expiry, and pull latency must not erase the last known-good local image; test failure and rollback behavior before scheduling unattended updates.
podman ps --filter name=app --format '{{.Names}} {{.Status}} {{.Image}}'
podman inspect app --format 'image={{.ImageName}} health={{.State.Health.Status}} restart={{.HostConfig.RestartPolicy.Name}}'
podman stats --no-stream app
podman logs --since 15m --tail 200 app
podman auto-update --dry-run
systemctl --user show app.service -p ActiveState -p SubState -p NRestarts Back up data consistently and prove recovery
Container deletion, image archives, and container filesystem exports are not backups of named volumes. For databases and other stateful services, use the application's native backup or quiesce protocol; stopping the writer creates an outage but is the simplest portable consistency boundary. Export every required named volume, save the exact image or record its digest, retain Quadlet configuration separately, protect archives like production data, and test restoration into an isolated name and network. Do not rely on an export created while writes were active unless the application explicitly guarantees crash-consistent recovery.
state_dir=${XDG_STATE_HOME:-$HOME/.local/state}
backup_dir=$state_dir/backups/podman/app-2026-08-12
install -d -m 0700 "$backup_dir"
systemctl --user stop app.service
trap 'systemctl --user start app.service' EXIT
podman volume export app-data --output "$backup_dir/app-data.tar"
podman save --format oci-archive --output "$backup_dir/app-image.tar" registry.example.com/team/app@sha256:DIGEST
install -m 0600 "$HOME/.config/containers/systemd/app.container" "$backup_dir/app.container"
sha256sum "$backup_dir/app-data.tar" "$backup_dir/app-image.tar" "$backup_dir/app.container" > "$backup_dir/SHA256SUMS"
systemctl --user start app.service
trap - EXIT
sha256sum -c "$backup_dir/SHA256SUMS" state_dir=${XDG_STATE_HOME:-$HOME/.local/state}
backup_dir=$state_dir/backups/podman/app-2026-08-12
sha256sum -c "$backup_dir/SHA256SUMS"
podman load --input "$backup_dir/app-image.tar"
podman volume create app-data-restore-test
podman volume import app-data-restore-test "$backup_dir/app-data.tar"
podman run --rm --network none \
--mount type=volume,source=app-data-restore-test,target=/data,ro \
docker.io/library/alpine@sha256:DIGEST find /data -xdev -maxdepth 2 -type f -print Troubleshoot without erasing evidence or weakening isolation
Start with version, inspect, events, journald, storage, network, cgroup, and mandatory-access-control evidence. A permission denial is not a reason to add --privileged, disable SELinux or AppArmor, or expose the API socket. Rootless containers may fail because subordinate IDs, filesystem type, cgroup delegation, low-port policy, or user-service lifetime differ from the tested host. podman system check can detect storage problems, but repair, reset, prune, volume removal, and network removal can destroy recoverable state. Back up and understand the scope before any repair command.
podman version
podman info --debug
podman inspect app
podman port app
podman network inspect app-net
podman volume inspect app-data
podman system df
podman system check --quick
journalctl --user -u app.service --since -30min --no-pager
ausearch -m AVC,USER_AVC -ts recent 2>/dev/null || true Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- Podman projectpodman(1) — Pod Managerdocs.podman.io
- Podman projectpodman-run(1) — Run a process in a new containerdocs.podman.io
- Podman projectpodman-systemd.unit(5) — Quadlet systemd unitsdocs.podman.io
- Podman projectpodman-secret(1) — Manage Podman secretsdocs.podman.io
- Podman projectpodman-auto-update(1) — Auto-update containersdocs.podman.io
- Podman projectpodman-volume-export(1) — Export volume datadocs.podman.io
- Podman projectpodman-save(1) — Save images to an archivedocs.podman.io
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



