Podman is an OCI-compatible container engine: it runs the same images and follows much of the same CLI shape as Docker, but it does not rely on a single long-running daemon. Each podman command talks to your user (or root) process space, which makes Podman a natural fit for systemd units and for rootless workflows on Red Hat–family systems.
This guide covers Rocky Linux 9 and AlmaLinux 9 (and largely applies to RHEL 9 and similar derivatives). You will install Podman from AppStream, run a quick Rocky Linux image, deploy PostgreSQL with a persistent host directory, then register the container as a systemd service the supported way using podman generate systemd.
Related posts
- How to install and use Podman in Rocky Linux / Alma Linux / CentOS 8
- How to install and use Podman in Debian 11
- How to install and use Podman in Fedora 34/35
- How to install and use Podman in OpenSUSE Leap 15.3
- How to install and configure Docker in Rocky Linux / CentOS 8
- Getting started with Docker Compose (with examples)
Prerequisites
- Rocky Linux 9 or AlmaLinux 9 (fully updated)
- sudo access (this walkthrough uses rootful Podman for the PostgreSQL + systemd examples; rootless notes are below)
- Network access to pull images (e.g.
docker.io)
What you will do
- Update the host and install Podman (and optional Docker-compatible shim).
- Verify the installation and pull a small Rocky Linux container.
- Run PostgreSQL with a bind-mounted data directory and the
:ZSELinux label. - Generate a correct systemd unit with
podman generate systemd --new.
Why Podman instead of Docker (short)
| Topic | Podman (typical) | Docker Engine (typical) |
|---|---|---|
| Daemon | No central daemon required | dockerd runs continuously |
| CLI | podman … (often mirroring docker …) | docker … |
| Rootless | First-class: unprivileged users can run containers | Possible but not the default mental model |
| Pods | Native podman pod | Swarm / orchestrators |
Podman works alongside Buildah (image builds) and Skopeo (copy/inspect/sign images). For scripts and tutorials that still say docker, you can install podman-docker so /usr/bin/docker invokes Podman (see below).
1. Update the system
| |
Optional editor or tools:
| |
2. Install Podman
Podman ships in the default AppStream repository on Rocky Linux 9 and AlmaLinux 9:
| |
Optional — Docker-compatible command name
If you want existing scripts or muscle memory that call docker to use Podman underneath:
| |
Dependencies you may see include crun (or runc), containers-common, Skopeo-related packages, and SELinux policy modules for containers—notably different from older articles that mentioned atomic-registries, which is not the focus on EL9.
Verify the installation
| |
Rootless check: as a normal user (without sudo), podman info should show rootless: true. The PostgreSQL and systemd examples later use sudo podman so the unit runs as system services with predictable paths; for development, rootless Podman is often enough.
3. Basic CLI: pull, run, list, remove
Most docker subcommands map directly: run, ps, pull, images, rm, exec, etc.
Run an interactive Rocky Linux shell
If you do not have Docker installed, docker run … will fail; the Podman equivalent is:
| |
If your mirror or docs still use docker.io/rockylinux/rockylinux:9 or a minor tag like :9.4, either form is fine as long as the tag exists in the registry.
Inside the container you should see a shell as root (unless you add --user). Exit with exit or Ctrl+D.
List containers and images
| |
Remove a container and an image
| |
Tip: --rm on podman run deletes the container automatically when it exits—handy for one-off tests.
4. PostgreSQL with a persistent host directory
Container filesystems are ephemeral unless you attach storage. Here the data directory is a directory on the host, bind-mounted into the container.
Create a data directory
Pick a path (examples use a dedicated tree under /srv for system-wide services; you can use $HOME for experiments):
| |
SELinux and the :Z suffix
On Rocky/Alma with SELinux enforcing, bind mounts often need a relabel so the container process can write the directory. Appending :Z to the volume tells Podman to relabel the content for private container use (do not share the same host path with two unrelated containers using :Z on both).
Run PostgreSQL (do not reuse example passwords in production)
Replace YOUR_SECURE_PASSWORD with a strong secret (or load variables from a root-only file):
| |
Check status and logs:
| |
Connect with podman exec
| |
Stop and remove
Graceful stop:
| |
Remove the container (data remains on the host under /srv/postgres-podman/data):
| |
Force-remove a stuck container:
| |
5. systemd: use podman generate systemd
The old pattern of hand-writing ExecStart=/usr/bin/podman run … is easy to get wrong: the original article omitted --name, left [Install] incomplete, and embedded passwords in the unit. On current Podman, the supported approach is to generate units.
Clean up any test container
| |
Create a named, long-lived container (no --rm)
Adjust paths and credentials to match your environment:
| |
Generate a system service (--files writes container-postgres-podman.service in the current directory):
| |
What --new does: systemd will podman run a fresh container instance according to the unit instead of only attaching to an already-running ID—this plays nicely with reboots and systemctl restart.
Reload and enable:
| |
View logs:
| |
Secrets and production hygiene
- Prefer
EnvironmentFile=-/etc/default/postgres-podman(mode600) instead of inline-e POSTGRES_PASSWORD=…in a world-readable unit fragment, or use your secrets manager. - Bind to
127.0.0.1:5432:5432if only local apps should reach Postgres. - Keep
postgres:14-alpinepinned by digest in real deployments so upgrades are deliberate.
Rootless Podman (quick notes)
- Rootless containers use subuids/subgids; storage lives under your user (
~/.local/share/containers). - Publishing ports below 1024 may require root or
sysctl/firewalldtuning. - For systemd –user services, you can generate units into
~/.config/systemd/user/and useloginctl enable-lingerif you need services to survive logout—seepodman generate systemd --helpfor--user.
Troubleshooting
| Issue | What to check |
|---|---|
| Permission denied on volume | SELinux: add :Z or :z appropriately; confirm host directory ownership. |
| Cannot pull image | Proxy/firewall; registry mirrors; podman login for private registries. |
| Port already in use | Another Postgres or container on 5432; change -p mapping. |
| systemd fails immediately | journalctl -xeu container-postgres-podman.service; ensure --name matches the generated unit; run podman ps -a. |
docker: command not found | Install podman-docker or call podman explicitly. |
Conclusion
You can install Podman from AppStream on Rocky Linux 9 and AlmaLinux 9, use the familiar Docker-like workflow for development, persist databases with bind mounts and SELinux labels, and promote a container to a production-style service with podman generate systemd --new instead of fragile hand-written units.
If you are standardizing automation for many hosts, compare this flow with Ansible-driven Docker installs and your organization’s preference for Podman versus Docker CE on RHEL-family servers.