Chapter Objective: Pull and manage container images, run and manage containers with Podman, persist data with volumes, and run containers as managed systemd services using Quadlet.
Key commands: podman pull, podman run, podman ps, podman volume
What Is Podman?
Podman is RHEL's container engine — it builds, pulls, runs, and manages OCI-compliant containers, using the same image format and much of the same command syntax as Docker. Unlike Docker's client-server model with a constantly running background daemon, Podman is daemonless: each command runs as its own process, and containers are managed directly by the kernel.
Working with Images
# Search a configured registry for an image
podman search nginx
# Pull an image without running it yet
podman pull registry.access.redhat.com/ubi9/ubi
# List locally stored images
podman images
# Remove an image
podman rmi ubi9/ubi
# Inspect image metadata
podman inspect ubi9/ubi
registry.access.redhat.com, are freely redistributable and built on the same foundation as RHEL itself — a solid default choice for images you build on top of in a Red Hat environment.
Running and Managing Containers
# Run a container interactively, removing it automatically when it exits
podman run -it --rm ubi9/ubi /bin/bash
# Run a container in the background (detached), mapping a port
podman run -d --name webserver -p 8080:80 nginx
# List running containers
podman ps
# List ALL containers, including stopped ones
podman ps -a
# View a container's logs
podman logs webserver
# Stop, start, or remove a container
podman stop webserver
podman start webserver
podman rm webserver
# Get an interactive shell inside a RUNNING container
podman exec -it webserver /bin/bash
podman rm (or run with --rm to begin with). podman ps -a often reveals a pile of stopped containers a system administrator forgot were still there.
Rootless Containers
A regular, unprivileged user can run Podman containers without any special setup — this is Podman's default and recommended mode on RHEL, called rootless. Inside the container, a process may appear to run as root, but it's actually mapped to an unprivileged UID on the host through user namespaces.
# A regular user (no sudo needed) can run containers directly
podman run -d --name myapp -p 8080:80 nginx
sudo podman (rootful) only when a specific container genuinely requires host-level privileges — binding to a port below 1024 without special configuration, for example. Otherwise, rootless is the safer and generally recommended default.
Persisting Data with Volumes
A container's own filesystem is ephemeral — removing the container discards any changes made inside it. Volumes provide storage that persists independently of any one container's lifecycle.
# Create a named volume
podman volume create webdata
# Mount it into a container
podman run -d --name webserver -p 8080:80 -v webdata:/usr/share/nginx/html nginx
# Mount a host directory directly instead of a named volume
podman run -d --name webserver -p 8080:80 -v /srv/web:/usr/share/nginx/html:Z nginx
# List volumes
podman volume ls
# Remove a volume no longer needed
podman volume rm webdata
:Z tells Podman to apply an appropriate private SELinux label to that content for the container — without it, SELinux (Chapter 6) may deny the container access to the mounted directory entirely.
Container Networking
# Map a host port to a container port (host:container)
podman run -d -p 8080:80 nginx
# List configured networks
podman network ls
# Create a custom network so containers can reach each other by name
podman network create appnet
podman run -d --name db --network appnet postgres
podman run -d --name api --network appnet myapi
Running Containers as systemd Services with Quadlet
For a container that should behave like any other managed service — starting at boot, restarting on failure, logging through the journal — RHEL's current recommended approach is Quadlet: a declarative .container file that systemd's generator turns into a full service unit automatically.
# Rootless quadlet files live here (create the directory if needed)
mkdir -p ~/.config/containers/systemd/
cat > ~/.config/containers/systemd/webserver.container << 'EOF'
[Unit]
Description=Nginx Web Server
[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=webdata.volume:/usr/share/nginx/html
[Service]
Restart=always
[Install]
WantedBy=default.target
EOF
# Reload systemd so it picks up the new quadlet definition
systemctl --user daemon-reload
# Manage it exactly like any other systemd service (note the .service suffix)
systemctl --user enable --now webserver.service
systemctl --user status webserver.service
journalctl --user -u webserver.service
podman generate systemd, produced a static unit file from an already-running container — convenient once, but prone to drifting out of sync with reality. Quadlet regenerates the service definition fresh from the source file every time, which is why it's now the recommended path.
loginctl enable-linger username so it can start at boot and keep running without an active login session.
Key Terms for Chapter 17
- Podman
- RHEL's daemonless container engine, compatible with the OCI image format
- rootless container
- A container run by an unprivileged user, with in-container "root" mapped to a non-privileged host UID
- UBI (Universal Base Image)
- Red Hat's freely redistributable base container images
- volume
- Persistent storage that survives independently of any single container's lifecycle
- :Z suffix
- Bind-mount option applying a private SELinux label so a container can access host directory content
- Quadlet
- A declarative
.container/.volume/.networkfile format that systemd's generator converts into managed service units
Review Questions
- What is the key architectural difference between Podman and a daemon-based container engine?
- Why does running containers rootless improve security compared to running them as root?
- What's the difference between stopping a container with
podman stopand removing it withpodman rm? - What does appending
:Zto a bind-mounted volume accomplish on a RHEL system? - Why might two containers attached to the same custom Podman network be able to reach each other by name?
- What advantage does Quadlet have over the older
podman generate systemdapproach? - Why is
loginctl enable-lingerrelevant to a rootless Quadlet service that should start at boot?