🦭 Podman Basics

Containers Series: Part 1 — Podman Basics  |  Part 2 — Docker Basics  |  Part 3 — Container Networking

What is Podman?

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.

Core Concepts

Image

Read-only template for a container. Pulled from a registry like docker.io or quay.io.

Container

A running instance of an image. Isolated process with its own filesystem, network, PID space.

Registry

Remote store for images. docker.io, quay.io, registry.access.redhat.com.

Volume

Persistent storage that survives container removal. Mounted into the container filesystem.

Pod

Group of containers sharing network namespace. Podman's Kubernetes-compatible unit.

Rootless

Containers run as your UID — no root required. Podman's default and key security feature.

Examples

1
Install Podman and First Run
# 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:8080
podman ps output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 nginx:latest nginx -g daemon o... 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp webserver
Flags explained: -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.
2
Managing Images
# 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 -a
podman images output:
REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/library/nginx latest a72860cb95fd 2 weeks ago 192 MB docker.io/library/fedora 39 b85def8c2d8a 3 weeks ago 185 MB
💡 Rootless image storage: When running as a regular user, images are stored in ~/.local/share/containers/storage/ — not in a system-wide location. Each user has their own image cache.
3
Managing Containers
# 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
Containers vs Images: podman rm removes a container (the running instance). podman rmi removes an image (the template). Removing a container does not remove its image.
4
Interacting with Running Containers
# 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 is your Swiss Army knife. You never need to SSH into a container — podman exec -it gives you a shell directly. Combined with podman logs these two commands cover 90% of container troubleshooting.
5
Volumes — Persistent Storage
# 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
⚠️ SELinux and bind mounts on RHEL: Without the :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.
6
Build a Custom Image — Containerfile

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
💡 Containerfile vs Dockerfile: They are identical in syntax. Podman supports both filenames. If you have existing Dockerfiles they work with Podman without any changes — just run podman build instead of docker build.
7
Run Containers as systemd Services

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
This is the sysadmin way to run containers. Rather than learning a separate orchestration tool for simple single-host deployments, 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.
8
Podman vs Docker — Command Comparison

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 CommandPodman EquivalentNotes
docker runpodman runIdentical syntax
docker pspodman psIdentical
docker imagespodman imagesIdentical
docker pullpodman pullIdentical
docker buildpodman buildIdentical — uses Containerfile or Dockerfile
docker execpodman execIdentical
docker logspodman logsIdentical
docker stop/startpodman stop/startIdentical
docker-composepodman-composeSeparate install: dnf install podman-compose
docker swarmN/AUse Kubernetes or podman play kube instead
docker system prunepodman system pruneIdentical
💡 The biggest difference you will notice: No daemon. With Docker, 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).

Quick Reference

CommandWhat it does
podman run -d --name N -p H:C imageRun container detached with name and port mapping
podman run -it --rm image bashInteractive shell, remove on exit
podman psList running containers
podman ps -aList all containers including stopped
podman imagesList local images
podman pull image:tagPull image from registry
podman stop/start NAMEStop or start a container
podman rm NAMERemove a stopped container
podman rmi IMAGERemove an image
podman exec -it NAME bashShell into running container
podman logs -f NAMEFollow container logs
podman statsLive resource usage
podman inspect NAMEFull container/image details
podman build -t name:tag .Build image from Containerfile
podman generate systemd --name NGenerate systemd unit file
podman system pruneClean up unused containers, images, volumes
podman volume lsList named volumes
podman search IMAGESearch registries for image

← Back to Containers Index ↑ Back to EXPANDED