| name | podman |
| description | Rootless container management with Podman including Docker CLI compatibility, systemd integration, pod management, Quadlet, and Podman Compose for research environments. |
| metadata | {"references":["references/rootless-containers.md","references/docker-compatibility.md"],"assets":["assets/podman-compose-example.yml","assets/quadlet-example.container"]} |
Podman
A comprehensive guide to using Podman for rootless, daemonless container management in research environments. Podman provides a Docker-compatible CLI without requiring a root-level daemon, making it a natural fit for shared HPC clusters, institutional workstations, and security-conscious deployments. This skill covers the Docker-to-Podman migration path, rootless container setup, pod management, systemd integration via Quadlet, Podman Compose, building images with Buildah, and registry operations.
Resources in This Skill
This skill includes supporting materials for Podman-based container workflows:
References (detailed guides -- consult the table of contents in each file and read specific sections as needed):
references/rootless-containers.md - Rootless container architecture: user namespace mapping, subuid/subgid configuration, storage drivers, rootless networking (slirp4netns, pasta), volume permissions, cgroup v2 requirements, and troubleshooting
references/docker-compatibility.md - Docker compatibility layer: CLI compatibility matrix, Dockerfile support, Compose compatibility, socket emulation with podman-docker, known differences in networking/volumes/build behavior, and a migration checklist
Assets (ready-to-use templates):
assets/podman-compose-example.yml - Research stack compose file adapted for Podman with JupyterLab, REST API, and PostgreSQL
assets/quadlet-example.container - Systemd Quadlet unit file for auto-starting a containerized research service
Quick Reference Card
Docker-to-Podman Command Mapping
| Docker Command | Podman Equivalent | Notes |
|---|
docker run | podman run | Identical syntax |
docker build | podman build | Uses Buildah under the hood |
docker compose up | podman compose up | Requires podman-compose or docker-compose with Podman socket |
docker ps | podman ps | Identical syntax |
docker images | podman images | Identical syntax |
docker pull | podman pull | Searches multiple registries by default |
docker push | podman push | Identical syntax |
docker exec | podman exec | Identical syntax |
docker logs | podman logs | Identical syntax |
docker inspect | podman inspect | Identical syntax |
docker volume | podman volume | Identical syntax |
docker network | podman network | Uses Netavark by default (not Docker bridge) |
docker login | podman login | Identical syntax |
docker tag | podman tag | Identical syntax |
docker save/load | podman save/load | Identical syntax |
docker system prune | podman system prune | Identical syntax |
| N/A |
Essential Podman Commands
podman run -d --name myapp -p 8000:8000 myimage:latest
podman stop myapp
podman rm myapp
podman start myapp
podman info --format '{{.Host.Security.Rootless}}'
podman unshare cat /proc/self/uid_map
podman pod create --name research -p 8888:8888 -p 8000:8000
podman run -d --pod research --name jupyter jupyter/scipy-notebook
podman pod stop research
podman pod rm research
podman system info
podman system prune --all --force
podman system migrate
When to Use
Use this skill when you need to:
- Run containers without root privileges on shared infrastructure (HPC, lab workstations)
- Migrate an existing Docker workflow to Podman
- Manage containers with systemd integration for auto-start on boot
- Group related containers into pods (Kubernetes-style)
- Use Quadlet to declare containers as systemd units
- Build OCI images without a Docker daemon
- Run containers on a system where Docker is not installed or not permitted
- Generate Kubernetes YAML from running containers for deployment migration
Podman vs Docker
Podman and Docker both manage OCI containers, but they differ in architecture and security posture.
Architecture
| Aspect | Docker | Podman |
|---|
| Daemon | Central daemon (dockerd) running as root | No daemon; each command is a fork-exec |
| Root requirement | Daemon runs as root; rootless mode is optional | Rootless by default |
| Process model | Containers are children of the daemon | Containers are children of the calling process |
| Init system | Requires separate restart config | Native systemd integration via Quadlet |
| Compose | docker compose (built-in plugin) | podman compose (wrapper) or podman-compose (Python) |
| Swarm | Built-in orchestration | Not supported; use Kubernetes |
| Build engine | BuildKit | Buildah (integrated) |
| Image format | OCI / Docker | OCI / Docker |
When Docker Might Be Better
- You need Docker Swarm for orchestration
- Your CI/CD pipeline is tightly coupled to the Docker socket
- Your team is already productive with Docker and has no need to change
- You depend on Docker Desktop features (GUI, extensions, Dev Environments)
When Podman Is the Better Choice
- Your institution prohibits root-level daemons on shared systems
- You want containers to start and stop with systemd
- You need Kubernetes YAML generation from running containers
- You want a daemonless architecture (no single point of failure)
- You are working on RHEL, CentOS Stream, or Fedora where Podman ships by default
Rootless Container Setup
Rootless mode runs containers entirely within a user's namespace, requiring no elevated privileges. This is Podman's default mode.
Prerequisites
sysctl user.max_user_namespaces
grep $USER /etc/subuid
grep $USER /etc/subgid
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
podman system migrate
First Run
podman info --format '{{.Host.Security.Rootless}}'
podman run --rm docker.io/library/alpine echo "Rootless works"
See references/rootless-containers.md for detailed coverage of user namespace mapping, storage drivers, networking, and volume permissions.
Docker CLI Compatibility
Alias Approach
The simplest migration path is to alias docker to podman:
alias docker=podman
This works for most single-container workflows. Commands like docker run, docker build, docker pull, and docker exec are fully compatible.
Podman-Docker Package
For deeper compatibility, install the podman-docker package, which provides a docker binary that calls podman and emulates the Docker socket:
sudo dnf install podman-docker
sudo apt install podman-docker
sudo systemctl enable --now podman.socket
Socket Emulation
Some tools (like VS Code Dev Containers or Testcontainers) require a Docker-compatible socket. Podman can provide one:
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
sudo systemctl enable --now podman.socket
Compose Compatibility
Podman supports Compose through two paths:
podman compose up -d
pip install podman-compose
podman-compose up -d
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
docker-compose up -d
See references/docker-compatibility.md for the full CLI compatibility matrix and known behavioral differences.
Podman Compose vs Docker Compose
Podman Compose supports most Docker Compose features but has some differences:
| Feature | Docker Compose | Podman Compose |
|---|
depends_on with conditions | Full support | Supported (podman-compose 1.1+) |
profiles | Full support | Supported |
build | BuildKit | Buildah |
secrets (file-based) | Full support | Supported |
networks (custom drivers) | Full support | Netavark only |
deploy (resource limits) | Swarm/Compose v2 | Limited |
| Watch mode | docker compose watch | Not available |
| GPU passthrough | deploy.resources.reservations.devices | --device via CLI or CDI |
podman compose config
podman compose up -d
podman compose logs -f
podman compose down
See assets/podman-compose-example.yml for a research stack template adapted for Podman.
Pod Management
Pods are a Podman-specific concept (borrowed from Kubernetes) that groups multiple containers sharing a network namespace and optionally other namespaces. All containers in a pod share localhost.
Creating and Using Pods
podman pod create --name research-stack \
-p 8888:8888 \
-p 8000:8000 \
-p 5432:5432
podman run -d --pod research-stack \
--name jupyter \
-e JUPYTER_TOKEN=changeme \
quay.io/jupyter/scipy-notebook:latest
podman run -d --pod research-stack \
--name api \
myresearch-api:latest
podman run -d --pod research-stack \
--name db \
-e POSTGRES_PASSWORD=changeme \
docker.io/library/postgres:16-alpine
Pod Lifecycle
podman pod list
podman pod inspect research-stack
podman pod top research-stack
podman pod stop research-stack
podman pod start research-stack
podman pod rm research-stack
podman pod rm -f research-stack
Generating Kubernetes YAML from Pods
podman generate kube research-stack > research-stack.yml
kubectl apply -f research-stack.yml
podman play kube research-stack.yml
Systemd Integration and Quadlet
Quadlet is the modern way to manage Podman containers with systemd. You write declarative .container files (similar to systemd unit files) and systemd manages the container lifecycle.
Quadlet File Locations
~/.config/containers/systemd/ # Rootless (per-user)
/etc/containers/systemd/ # Rootful (system-wide)
Using Quadlet
cp quadlet-example.container ~/.config/containers/systemd/research-api.container
systemctl --user daemon-reload
systemctl --user start research-api
systemctl --user enable research-api
loginctl enable-linger $USER
systemctl --user status research-api
journalctl --user -u research-api -f
See assets/quadlet-example.container for an annotated Quadlet unit file.
Legacy: podman generate systemd
The older approach generates systemd unit files from running containers. Quadlet is preferred for new setups.
podman generate systemd --new --name mycontainer > ~/.config/systemd/user/mycontainer.service
systemctl --user daemon-reload
systemctl --user enable --now mycontainer
Podman Machine (macOS / Windows)
On macOS and Windows, Podman runs containers inside a Linux virtual machine managed by podman machine.
podman machine init
podman machine start
podman machine list
podman machine info
podman machine ssh
podman machine stop
podman machine init --cpus 4 --memory 8192 --disk-size 100
podman machine set --rootful
macOS-Specific Notes
- Podman Desktop provides a GUI (alternative to Docker Desktop)
- File sharing between host and VM uses virtiofs (fast) or 9p
- The Podman socket is automatically forwarded to the host
- Rosetta 2 can run amd64 containers on Apple Silicon (with
--platform linux/amd64)
Building Images (Buildah)
Podman delegates image building to Buildah. All podman build commands use Buildah under the hood. You can also use Buildah directly for more advanced workflows.
Building with Podman
podman build -t myimage:latest .
podman build --platform linux/amd64,linux/arm64 -t myimage:latest .
podman build --build-arg PYTHON_VERSION=3.12 -t myimage:latest .
podman build -f Containerfile -t myimage:latest .
Buildah Direct Usage
container=$(buildah from python:3.12-slim)
buildah run $container pip install numpy pandas
buildah copy $container ./app /app
buildah config --entrypoint '["python", "-m", "myapp"]' $container
buildah commit $container myimage:latest
Containerfile vs Dockerfile
Podman accepts both Containerfile and Dockerfile. The syntax is identical. Containerfile is the OCI-standard name, and Podman searches for it first.
Registry Operations
Configuring Registries
Podman searches multiple registries by default. Configure them in /etc/containers/registries.conf or ~/.config/containers/registries.conf:
unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"]
Login and Push
podman login docker.io
podman login ghcr.io
podman login quay.io
podman tag myimage:latest ghcr.io/myorg/myimage:latest
podman push ghcr.io/myorg/myimage:latest
podman pull docker.io/library/python:3.12-slim
Skopeo for Registry Inspection
skopeo inspect docker://docker.io/library/python:3.12-slim
skopeo copy docker://docker.io/myimage:latest docker://ghcr.io/myorg/myimage:latest
skopeo copy docker://myimage:latest oci:./myimage-oci:latest
Common Mistakes
-
Expecting Docker socket at /var/run/docker.sock -- Podman's rootless socket is at $XDG_RUNTIME_DIR/podman/podman.sock. Enable it with systemctl --user enable --now podman.socket and set DOCKER_HOST accordingly.
-
Forgetting to configure subuid/subgid -- Rootless containers require subordinate UID/GID ranges. Without them, podman run fails with user namespace errors. Check /etc/subuid and /etc/subgid.
-
Using --privileged to fix permission errors -- This defeats the purpose of rootless containers. Instead, understand user namespace ID mapping and set volume ownership correctly.
-
Ignoring cgroup v2 requirements -- Rootless resource limits (CPU, memory) require cgroup v2. On older systems with cgroup v1, these flags are silently ignored.
-
Confusing Podman Compose with Docker Compose -- They are separate tools. podman compose is a thin wrapper that delegates to docker-compose or podman-compose. Install one of them first.
-
Not enabling linger for user services -- Without loginctl enable-linger, rootless systemd services (including Quadlet containers) stop when the user logs out.
-
Assuming Docker volumes carry over -- Podman stores volumes in a different location than Docker. Migrating from Docker requires re-creating volumes and copying data.
-
Using podman generate systemd for new setups -- This command is deprecated. Use Quadlet .container files instead for systemd integration.
-
Forgetting podman machine start on macOS -- Unlike Docker Desktop, Podman does not always auto-start the VM. Run podman machine start after a reboot or check with podman machine list.
-
Pulling unqualified image names without configuring registries -- Running podman pull python:3.12 prompts interactively for which registry to use. Configure unqualified-search-registries in registries.conf or use fully qualified names like docker.io/library/python:3.12.
Best Practices
Resources
Official Documentation
Rootless and Security
Systemd and Quadlet
Compose and Pods
Migration