🦭 Podman Containers Deep Dive
RHCSA RHEL 10 — Images · Containers · Volumes · Rootless · Pods · Registry · Production
1. Podman Overview (L1)
Daemonless · Rootless · OCI · Docker-compatible container engine for RHEL 8/9/10
What is Podman? Architecture vs Docker
Podman (Pod Manager) is a daemonless, rootless, OCI-compliant container engine — the default on RHEL 8/9/10.
| Feature | Docker | Podman |
|---|---|---|
| Architecture | Client-server — dockerd daemon required | Daemonless — fork/exec per command via conmon |
| Root requirement | Root or docker group required | Fully rootless — user namespace mapping |
| Security | Single root daemon = single attack point | No persistent daemon — no root process to exploit |
| CLI | docker CLI | podman CLI — drop-in compatible |
| Pods | Compose only | Native pod support (Kubernetes-compatible) |
| Systemd | Limited | Native — Quadlet unit files on RHEL 10 |
| K8s migration | docker-compose limited | podman generate kube → valid K8s YAML |
| OCI | Yes | Yes — OCI native |
podman CLI crashes, running containers are NOT affected — no parent daemon to kill them. Docker containers die if dockerd crashes.2. Image Management (L1)
Pull · Build · List · Search · Tag · Remove · Containerfile
Image Management — Pull, Build, List, Remove
ubi9-minimal as base on RHEL — Red Hat supported, smaller than full RHEL, freely redistributable. Use ubi-init when systemd inside the container is needed.3. Container Lifecycle (L1)
run · start · stop · restart · kill · rm — full lifecycle with all key options
Container Lifecycle — Full Reference
| Option | Description | Example |
|---|---|---|
| -d | Detached mode | -d |
| -it | Interactive terminal | -it ubi9 bash |
| --name | Name the container | --name webserver |
| -p | Port mapping host:container | -p 8080:80 |
| -v | Volume / bind mount | -v mydata:/app/data |
| -e | Environment variable | -e DB_HOST=localhost |
| --rm | Auto-remove on exit | --rm |
| --read-only | Read-only root filesystem | --read-only |
| --cap-drop | Drop Linux capabilities | --cap-drop ALL |
| --security-opt | Security options | --security-opt no-new-privileges |
| --restart | Restart policy | --restart always |
4. Container Interaction (L1)
exec · logs · inspect · top · stats · cp — interact with running containers
Container Interaction — exec, logs, inspect, stats
5. Container Ports & Networking (L1)
Port mapping · Network create · Container DNS · Custom networks
Container Ports & Networking
podman network create mynet) for container-to-container DNS.6. Volumes Management (L1)
Named volumes · Create · Inspect · Mount · Backup · Restore
Volumes Management
| Aspect | Named Volume | Bind Mount |
|---|---|---|
| Managed by | Podman | User (host filesystem) |
| Location | Container storage dir | Any host path |
| SELinux | Auto-labelled | Needs :Z or :z suffix |
| Use case | Database data, persistent state | Config files, source code |
| Syntax | -v volname:/path | -v /host/path:/path |
7. Bind Mounts (L1)
Host path mounting · SELinux :Z :z labels · Read-only mounts · RHEL SELinux context
Bind Mounts & SELinux Labels
| Label | Meaning | When to Use |
|---|---|---|
| :Z | Private — relabel for this container ONLY | Single container accessing the path |
| :z | Shared — relabel for multiple containers | Multiple containers sharing same host path |
| :ro | Read-only | Container must not modify host files |
| :Z,ro | Private + read-only | Config files for single container |
ausearch -m avc -ts recent8. Pods (L2)
Create pods · Add containers · K8s YAML export · Infra container
Pods — Kubernetes-style Container Groups
A Pod groups containers sharing the same network namespace, IPC, and optionally PID namespace — identical to Kubernetes pods. Port mappings go on the pod, not containers.
pause infra container that holds the network namespace. Inspect: podman ps -a --pod | grep infra9. Rootless Containers (L2)
User namespaces · subuid/subgid · Systemd integration · Quadlet (RHEL 10)
Rootless Containers — Architecture & Setup
- No root process — container UID 0 maps to your unprivileged host UID via user namespaces
- No persistent daemon — completely eliminates the dockerd root attack surface
- CI/CD safety — build/run containers in pipelines without giving root to CI agents
- Multi-user — each user has isolated, invisible container namespaces
10. Registry Operations (L2)
Login · Push · Pull · Logout · Registry config · Skopeo
Registry Operations
11. Container Commit & Export (L2)
commit · save · load · export · import — image portability and backup
Container Commit & Export
| Operation | Includes | Use Case |
|---|---|---|
podman save | Image layers + metadata | Offline image transfer between hosts |
podman export | Container filesystem only (flat) | Snapshot container filesystem |
podman commit | Running container → new image | Capture configured container |
podman push | Push to registry | Best practice for image sharing |
12. Docker Compatibility (L2)
Alias · Socket emulation · podman-compose · Docker → Podman migration
Docker Compatibility
| Docker | Podman | Notes |
|---|---|---|
| docker run | podman run | Identical syntax |
| docker-compose up | podman-compose up | Requires podman-compose |
| docker swarm | podman play kube | Use Kubernetes for orchestration |
| docker build | podman build | Containerfile or Dockerfile |
13. Troubleshooting (L2)
Common issues · Diagnosis commands · SELinux · Rootless · Storage
Troubleshooting — Issues, Diagnosis & Solutions
| Issue | Diagnosis | Solution |
|---|---|---|
| Container not starting | podman logs <c>podman inspect <c> | Check exit code + logs. Verify CMD exists in image. |
| Port already in use | ss -tulnp | grep <port> | Kill conflicting process or use different host port. |
| Image pull failed | podman pull <image> 2>&1 | Check internet/DNS, verify registry login. |
| Permission denied (rootless) | cat /etc/subuid | Add subuid/subgid entries + podman system migrate |
| Storage full | podman system df | podman system prune -a |
| Container exits immediately | podman ps -apodman logs <id> | PID1 must not exit. Debug: podman run --entrypoint bash myimage |
| SELinux permission denied | ausearch -m avc -ts recent | Add :Z to mount: -v /path:/cpath:Z |
| DNS not resolving | podman exec web cat /etc/resolv.conf | Create custom network: podman network create |
| Volume mount empty | podman inspect web | grep Mounts | Check host path exists, SELinux label, volume name. |
| OCI runtime error | podman run ... 2>&1 | Check crun: rpm -q crun |
14. System Management (L2)
prune · stats · info · version · systemd units · Quadlet (RHEL 10)
System Management
15. Real Production Scenarios (L3)
Application isolation · CI/CD pipelines · Security hardening · Disaster recovery
Deploy frontend (Nginx), backend API (Flask), and database (PostgreSQL) on RHEL 10. Each tier isolated, only frontend port exposed externally.
- How do you design the network topology?
- How do you handle DB persistence?
- How do you auto-restart on failure?
- Creates frontend-net (nginx+api) and backend-net (api+db). Only API connects to both — database has no external port mapping.
- Named volume for PostgreSQL:
podman volume create pgdata - Systemd unit files or Quadlet for auto-restart. Uses
--restart=always. - Generates K8s YAML with
podman generate kubefor documentation.
Jenkins/GitLab CI runner on shared RHEL 10 — build images, test in containers, push to registry — no root access on runner.
- How do you configure rootless Podman in CI?
- How do you authenticate non-interactively?
- How do you cache layers between runs?
- Rootless Podman: no sudo, no docker group, no root daemon.
- Auth via file:
podman login --authfile /tmp/auth.json registry.example.com. Credentials as CI secret. - Build args from env:
podman build --build-arg VERSION=$CI_SHA -t myapp . - Pre-pull base image to seed layer cache:
podman pull myapp:latestbefore build.
Production nginx container: no new privileges, read-only filesystem, dropped capabilities, non-root user, SELinux enforcing.
- What flags enforce these requirements?
- How does nginx write pid/tmp files if root filesystem is read-only?
podman run -d --read-only --security-opt no-new-privileges --cap-drop ALL --cap-add NET_BIND_SERVICE --user 1001:1001 nginx- Tmpfs for required write paths:
--tmpfs /var/run --tmpfs /var/cache/nginx --tmpfs /tmp - Verify:
podman inspect --format "{{.HostConfig.ReadonlyRootfs}}" web - Verify user:
podman exec web id— should show UID 1001, NOT root.
6 production containers on RHEL 10 must migrate to new hardware. No registry available (air-gapped environment). Minimal downtime.
- How do you capture container configurations?
- How do you transfer images without a registry?
- How do you preserve volume data?
- Save all images:
for img in $(podman images -q); do podman save -o images/$img.tar $img; done - Export volumes:
podman volume export volname -o volname.tar - Transfer via SCP/rsync. Import on new host:
podman load -i image.tar,podman volume import volname volname.tar - Verify:
podman ps,podman stats --no-stream, application health checks.
L1 → L3 Responsibility Matrix
Competency breakdown: Basic · Intermediate · Advanced
L1 → L3 Responsibility Matrix
| Level | Focus | Responsibilities | Key Commands |
|---|---|---|---|
| L1 Basic | Container Basics | Run containers, manage images, volumes & basic troubleshooting | run, ps, images, rmi, logs, exec |
| L2 Intermediate | Container Management | Networks, volumes, pods, registry, rootless, commit/export | network, volume, pod, commit, login, info |
| L3 Advanced | Production Admin | Design, secure, optimise, troubleshoot & automate | inspect, system df, prune, generate kube, events |
- Run named container detached
- List/start/stop/remove containers
- Pull and remove images
- View logs
- Map ports
- Create/mount named volumes
- Bind mounts with :Z
- Custom networks + DNS
- Build from Containerfile
- Rootless configuration
- Registry login/push
- Create and manage pods
- Generate systemd units
- Commit containers to images
- Multi-tier container architecture
- Security hardening flags
- K8s YAML generation
- CI/CD rootless integration
- Disaster recovery image save/load
- Quadlet systemd (RHEL 10)
- Performance tune + resource limits
Interview Questions — L2/L3
8 core interview questions with comprehensive model answers
Interview Q&A — 8 Core Questions with Model Answers
podman run -d --name web nginx — -d detaches from terminal. Container runs in background. View output: podman logs web. Attach: podman attach web. Container's PID 1 must be a long-running process — if it exits, the container stops.podman run -d -p 8080:80 nginx — format: -p HOST_PORT:CONTAINER_PORT. Traffic to localhost:8080 → container port 80. Bind to specific IP: -p 127.0.0.1:8080:80. Rootless: ports below 1024 need net.ipv4.ip_unprivileged_port_start sysctl.podman pod create --name mypod -p 8080:80. Add containers: podman run -d --pod mypod --name c1 nginx. Export to K8s: podman generate kube mypod.podman volume create mydata — Podman-managed storage. Mount: podman run -d -v mydata:/var/lib/mysql mariadb. Persists beyond container removal. Inspect mount path: podman volume inspect mydata. Backup: podman volume export mydata -o backup.tar. Rootless storage: $HOME/.local/share/containers/storage/volumes/./etc/subuid and /etc/subgid. Networking uses slirp4netns or pasta (RHEL 10) — userspace TCP/IP, no iptables root required. Cgroup v2 required for resource limits. Verify: podman info | grep rootless.podman ps -a — find exited container, note exit code. Step 2: podman logs <container> — read application error. Step 3: podman inspect <container> — check config, mounts, network. Step 4: podman events --since 1h — runtime events. Step 5: SELinux check: ausearch -m avc + add :Z. Step 6: Debug interactively: podman run --entrypoint /bin/sh myimage.podman save -o myimage.tar myimage:latest — exports all layers + metadata. Load: podman load -i myimage.tar. Air-gap workflow: save → SCP → load on target host. Alternative: podman export/import for filesystem-only snapshot (no image metadata). Best practice: use a registry with push/pull when possible.Deep-Dive Topics
cgroups v2 · Linux namespaces · Image layers · Storage drivers · Optimisation
Deep Dive — cgroups v2, Namespaces & Layers
| Namespace | Isolates | Podman Usage |
|---|---|---|
| PID | Process tree starting at PID 1 | Each container gets own PID namespace |
| Network | Interfaces, routing | Each container gets veth pair + bridge |
| Mount | Filesystem view | Container / = image layers (overlay) |
| IPC | SysV IPC, message queues | Isolated by default; shared in pods |
| UTS | Hostname | Container has own hostname |
| User | UID/GID mapping | Rootless maps UID 0 → host UID |
| Cgroup | Cgroup tree visibility | Container sees only its cgroup slice |
Quick Reference Cheat Sheet
40 essential Podman commands organised by category — L1 to L3
Quick Reference Cheat Sheet — All Essential Commands
| Category | Command | Description |
|---|---|---|
| Container | podman run -d --name web nginx | Run detached named container |
| Container | podman run -it ubi9 bash | Interactive shell |
| Container | podman run --rm alpine date | Run & auto-remove |
| Container | podman ps -a | List all containers |
| Container | podman stop/start/restart web | Lifecycle control |
| Container | podman rm -f web | Force remove |
| Container | podman container prune | Remove all stopped |
| Image | podman pull nginx:latest | Pull image |
| Image | podman images | List images |
| Image | podman rmi nginx:latest | Remove image |
| Image | podman image prune -a | Remove all unused |
| Image | podman build -t myapp:v1 . | Build from Containerfile |
| Image | podman save -o img.tar myapp:v1 | Export to tar |
| Image | podman load -i img.tar | Import from tar |
| Interact | podman exec -it web bash | Shell into container |
| Interact | podman logs -f web | Follow logs |
| Interact | podman inspect web | Full metadata JSON |
| Interact | podman stats | Live resource usage |
| Interact | podman cp web:/etc/nginx.conf . | Copy from container |
| Network | podman network create mynet | Create bridge network |
| Network | podman network ls | List networks |
| Network | podman port web | Show port mappings |
| Volume | podman volume create mydata | Create volume |
| Volume | podman volume ls/inspect/rm | Manage volumes |
| Volume | podman volume export/import | Backup/restore |
| Pod | podman pod create --name mypod -p 8080:80 | Create pod |
| Pod | podman pod ps | List pods |
| Pod | podman generate kube mypod | Export to K8s YAML |
| Pod | podman play kube mypod.yaml | Create from K8s YAML |
| Registry | podman login registry.redhat.io | Login to registry |
| Registry | podman push myapp:v1 registry.example.com/myapp:v1 | Push image |
| System | podman system df | Disk usage |
| System | podman system prune -a | Remove all unused |
| System | podman info | System config |
| System | podman events --since 1h | Recent events |
| Security | podman run --read-only --security-opt no-new-privileges --cap-drop ALL | Hardened run |
| Rootless | cat /etc/subuid && cat /etc/subgid | Check user namespace config |
| Rootless | podman system migrate | Apply rootless configuration |
| Systemd | podman generate systemd --name web | Generate unit file |
| Docker | alias docker=podman | Docker compatibility alias |