Podman is a daemonless container engine for developing, managing, and running OCI containers on Linux. Unlike Docker, Podman requires no root daemon — containers run as the user who launched them. It is the default container tool on RHEL 8+, Rocky Linux, and AlmaLinux, and is a drop-in replacement for Docker in most workflows.
Key advantages over Docker:
no daemon running as root, rootless containers by default, systemd integration
built in, and podman generate systemd to run containers as services.
For a sysadmin already comfortable with Linux security, Podman's model
feels natural.
Read-only template for a container. Pulled from a registry like docker.io or quay.io.
A running instance of an image. Isolated process with its own filesystem, network, PID space.
Remote store for images. docker.io, quay.io, registry.access.redhat.com.
Persistent storage that survives container removal. Mounted into the container filesystem.
Group of containers sharing network namespace. Podman's Kubernetes-compatible unit.
Containers run as your UID — no root required. Podman's default and key security feature.
# Install on RHEL 8/9, Rocky, AlmaLinux sudo dnf install -y podman # Install on Ubuntu/Debian sudo apt install -y podman # Verify installation podman --version podman info | head -20 # Run your first container — hello world podman run hello-world # Run an interactive bash session in a container podman run -it --rm fedora:latest bash # Run an nginx web server in the background podman run -d --name webserver -p 8080:80 nginx:latest # Verify it is running podman ps curl http://localhost:8080podman ps output:
-d = detached (background),
--name = give it a name,
-p 8080:80 = map host port 8080 to container port 80,
-it = interactive terminal,
--rm = remove container when it exits.
# Search for an image podman search nginx podman search --filter=is-official nginx # Pull an image without running it podman pull nginx:latest podman pull fedora:39 podman pull quay.io/centos/centos:stream9 # List local images podman images # Inspect an image podman inspect nginx:latest podman inspect --format '{{.Os}} {{.Architecture}}' nginx:latest # Show image history (layers) podman history nginx:latest # Remove an image podman rmi nginx:latest # Remove all unused images podman image prune # Remove ALL images podman rmi -apodman images output:
~/.local/share/containers/storage/
— not in a system-wide location. Each user has their own image cache.
# List running containers podman ps # List ALL containers including stopped podman ps -a # Stop a container gracefully (SIGTERM) podman stop webserver # Start a stopped container podman start webserver # Restart a container podman restart webserver # Kill a container immediately (SIGKILL) podman kill webserver # Remove a stopped container podman rm webserver # Remove a running container (force) podman rm -f webserver # Remove all stopped containers podman container prune # Rename a container podman rename webserver nginx-prod
podman rm removes
a container (the running instance). podman rmi removes
an image (the template). Removing a container does not remove its image.
# Execute a command in a running container podman exec webserver nginx -t # test nginx config podman exec webserver cat /etc/nginx/nginx.conf # Get an interactive shell in a running container podman exec -it webserver bash podman exec -it webserver sh # if bash not available # View container logs podman logs webserver podman logs -f webserver # follow (like tail -f) podman logs --tail 50 webserver # last 50 lines podman logs --since 1h webserver # last hour # Show container resource usage podman stats podman stats webserver # Show running processes inside container podman top webserver # Copy files to/from container podman cp webserver:/etc/nginx/nginx.conf ./nginx.conf podman cp ./myconfig.conf webserver:/etc/nginx/conf.d/
podman exec -it gives you
a shell directly. Combined with podman logs these two
commands cover 90% of container troubleshooting.
# Run container with a named volume (persists across container restarts) podman run -d \ --name db \ -v pgdata:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=secret \ postgres:15 # Run with a bind mount (map a host directory into the container) podman run -d \ --name webserver \ -v /var/www/html:/usr/share/nginx/html:ro \ -p 8080:80 \ nginx:latest # The :ro makes the mount read-only inside the container # :z or :Z fixes SELinux context on bind mounts (important on RHEL!) podman run -d \ -v /mydata:/data:z \ myapp:latest # Manage named volumes podman volume create mydata podman volume ls podman volume inspect mydata podman volume rm mydata podman volume prune # remove unused volumes
:z or :Z flag, SELinux will deny the container
access to bind-mounted host directories. Use :z for shared
mounts (multiple containers) or :Z for private mounts
(one container only). This is the most common "why can't my container
read my files" problem on RHEL/Rocky.
Podman uses Containerfile (identical format to Dockerfile):
# Example Containerfile for a simple web app cat > Containerfile << 'EOF' FROM fedora:39 LABEL maintainer="Craig <craig@binghamton.edu>" LABEL description="Simple Python web app" RUN dnf install -y python3 python3-pip && \ dnf clean all WORKDIR /app COPY requirements.txt . RUN pip3 install -r requirements.txt COPY . . EXPOSE 5000 CMD ["python3", "app.py"] EOF # Build the image podman build -t myapp:1.0 . podman build -t myapp:latest -f Containerfile . # Build with no cache (force fresh build) podman build --no-cache -t myapp:latest . # Tag an image podman tag myapp:latest myapp:1.0 podman tag myapp:latest quay.io/myuser/myapp:latest # Push to a registry podman login quay.io podman push quay.io/myuser/myapp:latest
podman build instead of docker build.
Podman's killer feature for sysadmins — generate a systemd unit file from a running container:
# Start the container first podman run -d \ --name nginx-prod \ --restart always \ -p 80:80 \ -v /var/www/html:/usr/share/nginx/html:z \ nginx:latest # Generate a systemd unit file podman generate systemd --name nginx-prod --files --new # This creates: container-nginx-prod.service # Install as a system service (root) sudo cp container-nginx-prod.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now container-nginx-prod # Install as a user service (rootless) mkdir -p ~/.config/systemd/user/ cp container-nginx-prod.service ~/.config/systemd/user/ systemctl --user daemon-reload systemctl --user enable --now container-nginx-prod # Enable lingering so user services survive logout loginctl enable-linger $USER # Manage like any systemd service sudo systemctl status container-nginx-prod sudo systemctl restart container-nginx-prod sudo journalctl -u container-nginx-prod -f
podman generate systemd
gives you a proper systemd service that starts on boot, restarts
on failure, and logs to journald — everything you already know
how to manage.
Podman is designed as a drop-in Docker replacement. Most commands are identical:
# If you have muscle memory for docker commands: alias docker=podman # Or install the docker-compatibility package sudo dnf install -y podman-docker
| Docker Command | Podman Equivalent | Notes |
|---|---|---|
| docker run | podman run | Identical syntax |
| docker ps | podman ps | Identical |
| docker images | podman images | Identical |
| docker pull | podman pull | Identical |
| docker build | podman build | Identical — uses Containerfile or Dockerfile |
| docker exec | podman exec | Identical |
| docker logs | podman logs | Identical |
| docker stop/start | podman stop/start | Identical |
| docker-compose | podman-compose | Separate install: dnf install podman-compose |
| docker swarm | N/A | Use Kubernetes or podman play kube instead |
| docker system prune | podman system prune | Identical |
systemctl status docker shows a running daemon.
With Podman there is no daemon — containers are direct child processes
of the shell that launched them. If you log out, rootless containers
stop (unless you enabled lingering with loginctl enable-linger).
| Command | What it does |
|---|---|
| podman run -d --name N -p H:C image | Run container detached with name and port mapping |
| podman run -it --rm image bash | Interactive shell, remove on exit |
| podman ps | List running containers |
| podman ps -a | List all containers including stopped |
| podman images | List local images |
| podman pull image:tag | Pull image from registry |
| podman stop/start NAME | Stop or start a container |
| podman rm NAME | Remove a stopped container |
| podman rmi IMAGE | Remove an image |
| podman exec -it NAME bash | Shell into running container |
| podman logs -f NAME | Follow container logs |
| podman stats | Live resource usage |
| podman inspect NAME | Full container/image details |
| podman build -t name:tag . | Build image from Containerfile |
| podman generate systemd --name N | Generate systemd unit file |
| podman system prune | Clean up unused containers, images, volumes |
| podman volume ls | List named volumes |
| podman search IMAGE | Search registries for image |