Podman and Docker share most CLI syntax. Key differences:
- Podman is daemonless and rootless by default — no background service required.
- Podman has native pod support (
podman pod) and can run Kubernetes YAML (podman kube play). - Podman includes
podman machinefor Linux VMs on macOS/Windows. - Docker has Docker Swarm for native clustering; Podman delegates clustering to Kubernetes.
Commands show Podman first (the preferred default on RHEL/Fedora), then the Docker equivalent. All commands validated against docs.podman.io and docs.docker.com.
Also see: kubectl & oc Cheatsheet
- Installation
- Version & Help
- Registry Login & Logout
- Images
- Running Containers
- Container Lifecycle
- Execute & Interact
- Logs
- Copy Files
- Build Images
- Volumes
- Networks
- Inspect & Stats
- Cleanup
- Podman — Pods
- Podman — Machine (macOS / Windows)
- Podman — Kubernetes YAML
- Podman — Systemd via Quadlet
- Docker Compose
- Private Registry — Kubernetes Secret
- Dockerfile Reference
Installation
Podman:
# RHEL / Fedora
sudo dnf install -y podman
# Ubuntu / Debian
sudo apt-get install -y podman
# macOS
brew install podman
podman machine init && podman machine start
Docker:
# RHEL / Fedora (requires Docker repo)
sudo dnf install -y docker-ce
sudo systemctl enable --now docker
# Ubuntu
sudo apt-get install -y docker-ce
sudo systemctl enable --now docker
# macOS — Docker Desktop from https://www.docker.com/products/docker-desktop
Version & Help
Podman:
podman --version
podman version # client info (no daemon needed)
podman info # host and runtime details
podman help
podman <command> --help
Docker:
docker --version
docker version # client and daemon info
docker info # system-wide info (alias: docker system info)
docker help
docker <command> --help
Registry Login & Logout
Podman:
podman login registry.example.com
podman login -u myuser -p mypassword registry.example.com
podman login -u myuser --password-stdin registry.example.com
# pipe password via stdin (safer than -p)
podman login --tls-verify=false registry.local
# skip TLS verification (non-production only)
podman login -u myuser -p ${TOKEN} quay.io
# TOKEN=$(oc whoami -t) for OpenShift registry
podman logout registry.example.com
podman logout -a # --all: remove all registry credentials
Docker:
docker login registry.example.com
docker login -u myuser -p mypassword registry.example.com
docker login -u myuser --password-stdin registry.example.com
docker logout registry.example.com
Images
Search
Podman:
podman search nginx
podman search quay.io/nginx
podman search --filter=is-official=true nginx
podman search --filter=stars=3 nginx
Docker:
docker search nginx
docker search --filter=is-official=true nginx
docker search --filter=stars=3 nginx
docker search --limit 10 nginx
docker search --format "{{.Name}}\t{{.StarCount}}" nginx
Pull
Podman:
podman pull nginx
podman pull nginx:1.27
podman pull quay.io/fedora/fedora:latest
podman pull --tls-verify=false registry.local/myimage
Docker:
docker pull nginx
docker pull nginx:1.27
docker pull --platform linux/arm64 nginx
List & Inspect
Podman:
podman images # list local images (alias: podman image ls)
podman images -a # include intermediate layers
podman image inspect nginx
podman history nginx
Docker:
docker images # alias: docker image ls
docker images -a
docker images --filter=dangling=true
docker images --format "{{.ID}}\t{{.Repository}}:{{.Tag}}"
docker image inspect nginx
docker history nginx
docker history --no-trunc nginx
Tag & Push
Podman:
podman tag nginx:latest quay.io/myuser/nginx:1.27
podman push quay.io/myuser/nginx:1.27
podman push --tls-verify=false registry.local/myimage:tag
Docker:
docker tag nginx:latest myuser/nginx:1.27
docker push myuser/nginx:1.27
Remove Images
Podman:
podman rmi nginx
podman rmi nginx:1.27
podman image prune # remove dangling images only
podman image prune -a # remove all unused images
podman image prune -f # skip confirmation
Docker:
docker rmi nginx
docker image prune # dangling images only
docker image prune -a # all unused images
docker image prune -f
Save & Load
Podman:
podman save -o myimage.tar myimage:tag
podman load -i myimage.tar
Docker:
docker image save -o myimage.tar myimage:tag
docker image load -i myimage.tar
Running Containers
Podman:
podman run nginx # foreground
podman run -d nginx # detached (background)
podman run -d --name webserver nginx
podman run -it --rm fedora /bin/bash
# interactive, auto-remove on exit
podman run -d -p 8080:80 nginx # host port 8080 → container port 80
podman run -d \
--name myapp \
-p 8080:8080 \
-e APP_ENV=production \
-v /host/data:/app/data:Z \
myimage:latest
# -e: environment variable
# -v ... :Z sets SELinux label (Podman-specific)
podman run --rm -v "$PWD":/work -w /work python:3.12 python script.py
# one-shot, current dir mounted
Key podman run flags:
-d, --detach background
-i, --interactive keep stdin open
-t, --tty pseudo-TTY
--rm remove container + anonymous volumes on exit
--name STRING
-p [[hostIP:]hostPort:]containerPort[/protocol]
-e KEY=VALUE (--env)
--env-file FILE
-v [[source:]dest[:options]] (:Z for SELinux label in Podman)
--network bridge|host|none|pasta|container:ID|NETNAME
--pod NAME join an existing pod (Podman-specific)
--restart no|always|on-failure[:n]|unless-stopped
-u, --user name|uid[:group]
-w, --workdir PATH
--entrypoint CMD
--privileged full host device access (disables isolation)
--cap-add CAPABILITY
--security-opt label=disable|no-new-privileges=true
--cpus DECIMAL
-m, --memory NUMBER[b|k|m|g]
Docker:
docker run nginx
docker run -d nginx
docker run -d --name webserver nginx
docker run -it --rm ubuntu /bin/bash
docker run -d -p 8080:80 nginx
docker run -d \
--name myapp \
-p 8080:8080 \
-e APP_ENV=production \
-v /host/data:/app/data \
--restart unless-stopped \
myimage:latest
Key docker run flags (same as Podman except where noted):
# Same: -d, -i, -t, --rm, --name, -p, -e, --env-file, -v, --network,
# --restart, -u, --workdir/-w, --entrypoint, --privileged,
# --cap-add, --cap-drop, --security-opt, --cpus, -m/--memory
# No --pod flag in Docker (Docker Swarm uses different grouping)
Container Lifecycle
Podman:
podman ps # running containers
podman ps -a # all containers
podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
podman start <container>
podman stop <container>
podman stop -t 5 <container> # wait 5s before SIGKILL
podman restart <container>
podman pause <container>
podman unpause <container>
podman rm <container>
podman rm -f <container> # force-remove running container
podman rm $(podman ps -aq) # remove all stopped containers
Docker:
docker ps
docker ps -a
docker ps -q # IDs only
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
docker container start <container>
docker container stop <container>
docker container stop -t 5 <container>
docker container restart <container>
docker container pause <container>
docker container unpause <container>
docker container kill <container> # default signal: KILL
docker container kill -s SIGTERM <container>
docker container rm <container>
docker container rm -f <container>
docker container rm -v <container> # -v also removes associated volumes
docker rm $(docker ps -aq)
Execute & Interact
Podman:
podman exec -it <container> /bin/bash
podman exec <container> env
podman exec -it <container> sh -c "ls /app"
podman exec -u root -it <container> /bin/bash
Docker:
docker container exec -it <container> /bin/bash
docker container exec <container> env
docker container exec -it <container> sh -c "ls /app"
docker container exec -u root -it <container> /bin/bash
Logs
Podman:
podman logs <container>
podman logs -f <container> # follow
podman logs --tail=100 <container>
podman logs --since=1h <container>
podman logs --timestamps <container>
Docker:
docker container logs <container>
docker container logs -f <container>
docker container logs -n 100 <container> # -n is short for --tail
docker container logs --tail=100 <container>
docker container logs --since=1h <container>
docker container logs --timestamps <container>
Copy Files
Podman:
podman cp /local/file <container>:/remote/path
podman cp <container>:/remote/file /local/path
Docker:
docker container cp /local/file <container>:/remote/path
docker container cp <container>:/remote/file /local/path
Build Images
docker build is an alias for docker buildx build (canonical form). Both are valid; flags are identical.
Podman:
podman build -t myimage:latest .
podman build -t myimage:latest -f Dockerfile.prod .
podman build --build-arg VERSION=1.2 -t myimage:1.2 .
podman build --no-cache -t myimage:latest .
podman build --layers -t myimage:latest .
# --layers: cache intermediate layers (default true)
podman build --platform linux/amd64 -t myimage .
podman build --target build-stage -t myimage .
podman build --pull=always -t myimage .
# --pull: always|missing|never|newer (policy string)
Docker:
docker buildx build -t myimage:latest . # canonical
docker build -t myimage:latest . # alias (same flags)
docker build -t myimage:latest -f Dockerfile.prod .
docker build --build-arg VERSION=1.2 -t myimage:1.2 .
docker build --no-cache -t myimage:latest .
docker build --target build-stage -t myimage .
docker build --platform linux/amd64,linux/arm64 -t myimage .
docker build --pull -t myimage .
# --pull is boolean in Docker (true/false)
docker build --progress=plain -t myimage .
# --progress: auto|tty|plain|quiet|rawjson
Commit a running container as a new image:
Podman:
podman commit <container> myimage:new-tag
Docker:
docker container commit [-m "message"] [-a "author"] <container> myimage:new-tag
Volumes
Podman:
podman volume create my-volume
podman volume ls
podman volume inspect my-volume
podman volume rm my-volume
podman volume prune # remove all unused volumes
podman run -d --name mydb \
-v my-volume:/var/lib/mysql \
mysql:8.0
Docker:
docker volume create my-volume
docker volume ls
docker volume inspect my-volume
docker volume rm my-volume
docker volume prune # remove unused local volumes
docker volume prune -a # include named volumes not referenced by a container
docker run -d --name mydb \
-v my-volume:/var/lib/mysql \
mysql:8.0
Networks
Podman:
podman network ls
podman network create my-net
podman network inspect my-net
podman network rm my-net
podman network prune
podman network connect my-net <container>
podman network disconnect my-net <container>
Docker:
docker network ls
docker network create my-net
docker network create -d bridge --subnet 172.20.0.0/16 my-net
docker network inspect my-net
docker network rm my-net
docker network prune
docker network connect my-net <container>
docker network disconnect -f my-net <container>
Inspect & Stats
Podman:
podman inspect <container>
podman inspect --format '{{.State.Status}}' <container>
podman inspect <image>
podman stats # live all containers
podman stats <container>
podman top <container>
Docker:
docker container inspect <container>
docker container inspect --format '{{.State.Status}}' <container>
docker image inspect <image>
docker container stats # live all containers
docker container stats --no-stream <container>
docker container top <container>
Cleanup
Podman:
podman system prune # stopped containers + dangling images + unused networks
podman system prune -a # + all unused images (not just dangling)
podman system prune --volumes # + unused volumes (excluded by default — data loss risk)
podman system prune -f # skip confirmation
podman system prune --build # also remove leftover build containers
podman system prune --external # clean storage remnants from unclean shutdowns
# --external is incompatible with -a and --filter
podman system df # disk usage breakdown
Docker:
docker system prune # stopped containers + dangling images + unused networks
docker system prune -a # + all unused images
docker system prune --volumes # + anonymous volumes
docker system prune -f
docker system df # disk usage breakdown
docker system df -v # verbose breakdown per object
Podman — Pods
Podman supports Kubernetes-style pods — a group of containers sharing network and IPC namespaces. No Docker equivalent.
podman pod create --name mypod
podman pod create --name mypod -p 8080:80
podman pod create --name mypod --network my-net
podman pod ps # list pods (also: podman pod ls / list)
podman pod inspect mypod
podman pod start mypod
podman pod stop mypod
podman pod stop -t 10 mypod
podman pod restart mypod
podman pod pause mypod
podman pod unpause mypod
podman pod kill mypod
podman pod kill -s SIGTERM mypod
podman pod rm mypod # stops and removes pod + all its containers
podman pod rm -f mypod
podman pod prune # remove all stopped pods
podman pod stats mypod
podman pod top mypod
podman pod logs mypod
# Run a container inside a pod
podman run -d --pod mypod --name app myimage
podman run -d --pod mypod --name db postgres:16
Podman — Machine (macOS / Windows)
podman machine runs a Linux VM so Podman works on macOS and Windows. Docker Desktop is the equivalent on those platforms.
podman machine init
podman machine init --cpus=4 -m 4096 --disk-size=60
# -m/--memory in MiB; --disk-size in GiB
podman machine init --now # start immediately after init
podman machine init --rootful # prefer rootful execution mode
podman machine init --image URL # custom VM image (flag is --image, not --image-path)
podman machine start
podman machine stop
podman machine restart
podman machine list # list VMs (also: podman machine ls)
podman machine info # active machine details
podman machine inspect
podman machine ssh # open a shell inside the VM
podman machine set # update machine settings
podman machine rm # remove default VM
podman machine rm -f myvm # force remove named VM
podman machine reset # reset all machines and environment
Troubleshooting — cannot connect:
podman system connection list
# Restart the machine
podman machine stop && podman machine start
# On macOS, if SSH_AUTH_SOCK causes connection failures
unset SSH_AUTH_SOCK
Podman — Kubernetes YAML
Run Kubernetes-compatible YAML locally without a cluster. No Docker equivalent.
# Play (run) a Kubernetes YAML file
podman kube play file.yaml
podman kube play file.yaml --start=false # create but don't start
podman kube play --down file.yaml # tear down pods from a previous run
podman kube play --replace file.yaml # tear down then recreate
podman kube play --network my-net file.yaml
podman kube play --tls-verify=false file.yaml
podman kube play --configmap configmap.yaml file.yaml
# Legacy alias (still valid):
podman play kube file.yaml
# Generate Kubernetes YAML from a running container or pod
podman kube generate mypod
podman kube generate -f output.yaml mypod
podman kube generate -s mypod # -s: include a Service object
podman kube generate --type deployment mypod # pod|deployment|daemonset|job
# Legacy alias (deprecated form, may still work):
# podman generate kube mypod
# Apply YAML to a running pod (update in place)
podman kube apply mypod
Podman — Systemd via Quadlet
podman generate systemd is deprecated (no new features; Quadlet is the replacement).
Quadlet generates systemd units from declarative .container, .pod, .volume, and .network files.
Unit file locations:
| Scope | Path |
|---|---|
| Rootless | ~/.config/containers/systemd/ |
| Root | /etc/containers/systemd/ |
Minimal .container unit file:
# ~/.config/containers/systemd/myapp.container
[Unit]
Description=My Application
[Container]
Image=quay.io/myuser/myapp:latest
ContainerName=myapp
PublishPort=8080:8080
Environment=APP_ENV=production
Volume=/data/myapp:/app/data:Z
Network=my-net.network
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target
# After creating or editing unit files
systemctl --user daemon-reload
systemctl --user start myapp
systemctl --user stop myapp
systemctl --user status myapp
systemctl --user enable myapp # start on login
systemctl --user disable myapp
# Debug Quadlet unit generation
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
Docker Compose
docker compose (space, not hyphen) is the built-in plugin since Docker CLI 20.10. Podman has podman compose (built-in since Podman 4.7+) or use podman-compose (third-party).
Docker:
docker compose up # create and start containers
docker compose up -d # detached
docker compose up --build # rebuild images before starting
docker compose up --remove-orphans
docker compose up --scale web=3
docker compose down # stop and remove containers, networks
docker compose down -v # also remove volumes
docker compose down --rmi all # also remove images
docker compose down --remove-orphans -t 30
docker compose ps # list containers in project
docker compose ps -a
docker compose ps -q
docker compose logs
docker compose logs -f
docker compose logs --tail=100 web
docker compose logs --timestamps
docker compose build
docker compose build --no-cache
docker compose pull
docker compose pull --ignore-pull-failures
docker compose exec web /bin/bash
docker compose run --rm web pytest
docker compose stop [SERVICE]
docker compose start [SERVICE]
docker compose restart [SERVICE]
docker compose config # validate and view merged compose file
Private Registry — Kubernetes Secret
kubectl create secret docker-registry registry-cred \
--docker-server=myregistry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
-n my-namespace
Reference in a Pod or Deployment spec (at the same level as containers):
spec:
imagePullSecrets:
- name: registry-cred
containers:
- name: myapp
image: myregistry.example.com/myapp:latest
Dockerfile Reference
A Dockerfile works identically for podman build and docker build.
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest
# base image — prefer UBI for Red Hat targets
LABEL org.opencontainers.image.authors="[email protected]" \
version="1.0"
ARG VERSION=1.0 # build-time variable (not available at runtime)
# Combine RUN commands to minimise layers
RUN microdnf install -y python3 && \
microdnf clean all
WORKDIR /app # working directory for subsequent instructions
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
COPY . .
ENV APP_PORT=8080 # available at build time and runtime
EXPOSE 8080 # documents the port; does not publish it
USER 1001 # run as non-root (required by OpenShift)
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["python3", "app.py"]
Dockerfile instruction reference:
| Instruction | Syntax | Purpose |
|---|---|---|
FROM | FROM [--platform=P] IMAGE[:TAG] [AS NAME] | Base image; starts a build stage |
RUN | RUN CMD or RUN ["exec","arg"] | Execute during build; creates a layer |
COPY | COPY [OPTIONS] SRC... DEST | Copy from build context into image |
ADD | ADD [OPTIONS] SRC... DEST | Like COPY; also unpacks local tars and fetches URLs |
CMD | CMD ["exec","arg1"] | Default command (overridable at docker run) |
ENTRYPOINT | ENTRYPOINT ["exec","arg1"] | Fixed entrypoint |
ENV | ENV KEY=VALUE | Runtime environment variable (persists in image) |
ARG | ARG NAME[=DEFAULT] | Build-time variable only (not in final image) |
EXPOSE | EXPOSE PORT[/PROTOCOL] | Documents a listening port; does not publish |
WORKDIR | WORKDIR /path | Working directory for subsequent instructions |
USER | USER user[:group] | Run-as identity |
VOLUME | VOLUME ["/path"] | Declares a mount point |
LABEL | LABEL key=value | Image metadata |
ONBUILD | ONBUILD INSTRUCTION | Deferred instruction executed in child builds |
HEALTHCHECK | HEALTHCHECK [OPTIONS] CMD cmd | Container health probe |
STOPSIGNAL | STOPSIGNAL signal | Signal sent to stop the container |
SHELL | SHELL ["exec","params"] | Override default shell for shell-form instructions |
MAINTAINER | MAINTAINER name | Deprecated — use LABEL org.opencontainers.image.authors= |
References: Podman commands · Docker CLI reference · Dockerfile reference · Podman Quadlet



