| Command | Description |
|---|---|
podman pull <image> |
Pull an image from a registry |
podman images |
List local images |
podman image inspect <image> |
Show detailed image info |
podman rmi <image> |
Remove an image |
podman tag <image> <new-tag> |
Tag an image |
podman build -t <name> . |
Build image from Dockerfile in current dir |
podman build -t <name> -f <Dockerfile> . |
Build from a specific Dockerfile |
# Run a container interactively (removed on exit)
podman run -it --rm <image> bash
# Run detached (background)
podman run -d --name <name> <image>
# Run with port mapping (host:container)
podman run -d -p 8080:80 <image>
# Run with environment variables
podman run -d -e MY_VAR=value <image>
# Run with a named volume mounted
podman run -d -v myvolume:/data <image>
# Run with a bind mount (host path)
podman run -d -v /host/path:/container/path:Z <image>
# :Z fixes SELinux labeling on Fedora/RHEL
# Run with multiple options combined
podman run -d \
--name myapp \
-p 3000:3000 \
-e NODE_ENV=production \
-v mydata:/app/data \
node:20Rootless tip: Podman runs containers as your user by default — no root required.
| Command | Description |
|---|---|
podman ps |
List running containers |
podman ps -a |
List all containers (including stopped) |
podman start <name|id> |
Start a stopped container |
podman stop <name|id> |
Gracefully stop a container |
podman kill <name|id> |
Force-kill a container |
podman restart <name|id> |
Restart a container |
podman rm <name|id> |
Remove a stopped container |
podman rm -f <name|id> |
Force-remove a running container |
podman rm $(podman ps -aq) |
Remove all stopped containers |
# View logs
podman logs <name>
podman logs -f <name> # Follow (tail) logs
# Get detailed container info
podman inspect <name>
# Show resource usage
podman stats
# Show running processes inside container
podman top <name>
# Show port mappings
podman port <name># Open an interactive shell in a running container
podman exec -it <name> bash
podman exec -it <name> sh # if bash is not available
# Run a one-off command
podman exec <name> ls /app
# Run as a specific user
podman exec -it -u root <name> bash# Create a volume
podman volume create myvolume
# List volumes
podman volume ls
# Inspect a volume (see mount path on host)
podman volume inspect myvolume
# Remove a volume
podman volume rm myvolume
# Remove all unused volumes
podman volume prune# Mount a host directory into a container
podman run -v /absolute/host/path:/container/path <image>
# Read-only bind mount
podman run -v /host/path:/container/path:ro <image>
# With SELinux relabeling (required on RHEL/Fedora for file access)
podman run -v /host/path:/container/path:Z <image>Use
:Zfor private (single container) mounts,:zfor shared (multiple containers).
- Rootless containers share a private network namespace.
- Containers on the same pod or explicitly the same network can communicate.
# List networks
podman network ls
# Create a custom network
podman network create mynet
# Connect a running container to a network
podman network connect mynet <name>
# Run a container on a specific network
podman run -d --network mynet --name app1 <image>
# Containers on the same network can reach each other by container name
# e.g. from app1: curl http://db:5432
# Inspect a network
podman network inspect mynet
# Remove a network
podman network rm mynetPods group containers that share the same network namespace (like Kubernetes pods).
# Create a pod with a port published
podman pod create --name mypod -p 8080:80
# Run containers inside the pod
podman run -d --pod mypod --name frontend nginx
podman run -d --pod mypod --name backend myapp
# List pods
podman pod ls
# Stop / start / remove a pod (and all its containers)
podman pod stop mypod
podman pod start mypod
podman pod rm -f mypodpodman-compose implements the Compose Spec — the same spec that Docker Compose v2 uses. In practice:
- Most
docker-compose.ymlfiles work unchanged withpodman-compose. - Not every Compose feature is implemented (some
deploy,build.cache_from, and Swarm-specific keys may be ignored or unsupported). - Podman-specific extensions can be added under the
x-podman:key (e.g.userns_mode,passwd). podman compose(the built-in subcommand) is a thin wrapper that delegates to whichever provider is installed —docker-composetakes precedence if both are installed.- The top-level
version:field is obsolete in the current Compose Spec and will trigger a warning — omit it.
Documentation
- Compose Spec reference: https://compose-spec.io/
- podman-compose source & docs: https://github.com/containers/podman-compose
- Podman built-in wrapper: https://docs.podman.io/en/latest/markdown/podman-compose.1.html
# Fedora / RHEL
sudo dnf install podman-compose
# Debian / Ubuntu
sudo apt install podman-compose
# pip (latest stable)
pip3 install --user podman-compose# compose.yaml (preferred filename; docker-compose.yml also works)
services:
<service-name>:
image: <image> # or use `build:` instead
container_name: <name> # optional; defaults to <project>_<service>_1
restart: unless-stopped
ports:
- "<host>:<container>"
environment:
- KEY=value
env_file:
- .env
volumes:
- <named-vol>:<container-path>
- ./relative/host/path:/container/path:Z
networks:
- <network-name>
depends_on:
- <other-service>
volumes:
<named-vol>: # declare named volumes here
networks:
<network-name>: # declare custom networks here| CLI | compose.yaml equivalent |
|---|---|
podman run -d nginx |
image: nginx |
-p 8080:80 |
ports: ["8080:80"] |
-e KEY=val |
environment: [KEY=val] |
-v mydata:/data |
volumes: [mydata:/data] + top-level volumes: {mydata:} |
-v $(pwd):/app:Z |
volumes: [.:/app:Z] |
--network mynet |
networks: [mynet] + top-level networks: {mynet:} |
--name myapp |
container_name: myapp |
--restart unless-stopped |
restart: unless-stopped |
Example — before (CLI):
podman run -d \
--name myapp \
-p 8080:80 \
-e NODE_ENV=production \
-v mydata:/app/data \
-v $(pwd):/app:Z \
node:20After (compose.yaml):
services:
myapp:
image: node:20
container_name: myapp
ports:
- "8080:80"
environment:
- NODE_ENV=production
volumes:
- mydata:/app/data
- .:/app:Z
volumes:
mydata:services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro,Z
depends_on:
- api
networks:
- frontend
api:
image: node:20
working_dir: /app
volumes:
- .:/app:Z
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
- db
networks:
- frontend
- backend
db:
image: postgres:16
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=mydb
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
volumes:
pgdata:
networks:
frontend:
backend:# Start all services (detached), building images if needed
podman compose up -d
# Start and force rebuild images
podman compose up -d --build
# Stop and remove containers (keeps volumes)
podman compose down
# Stop and remove containers AND named volumes
podman compose down -v
# View logs for all services
podman compose logs -f
# View logs for a single service
podman compose logs -f <service>
# Execute a command in a running service container
podman compose exec <service> bash
# Run a one-off command in a new container
podman compose run --rm <service> <command>
# List containers managed by this compose file
podman compose ps
# Pull latest images without starting
podman compose pull
# Restart a single service without touching others
podman compose restart <service>
# Scale a service to N replicas
podman compose up -d --scale <service>=3# .env file in the same directory is loaded automatically
# compose.yaml can reference variables with ${VAR} syntaxservices:
db:
image: postgres:${PG_VERSION:-16} # fallback default after :-
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}# .env
PG_VERSION=16
DB_PASSWORD=secretOverride for a single run without editing .env:
PG_VERSION=15 podman compose up -d# Add userns_mode so files written inside the container are owned by your
# host user (equivalent to --userns=keep-id on the CLI)
services:
myapp:
image: node:20
userns_mode: keep-id # podman-compose extension (x-podman not needed)
volumes:
- .:/app:ZWithout
userns_mode: keep-id, files created inside the container may appear owned by a different UID on the host.
# Remove all stopped containers
podman container prune
# Remove unused images
podman image prune
# Remove unused volumes
podman volume prune
# Remove everything unused (containers, images, volumes, networks)
podman system prune -a --volumesVSCode can attach to a running Podman container via the Dev Containers extension (previously "Remote - Containers"), with Podman as the container engine.
Search for Dev Containers (ms-vscode-remote.remote-containers) in the Extensions panel and install it.
Open Settings (Ctrl+,) and search for Dev Containers: Docker Path.
Set it to the Podman socket or binary:
// settings.json
{
"dev.containers.dockerPath": "podman"
}Alternatively, create a Docker-compatible socket so the extension uses the standard path:
# Enable the Podman socket (systemd user service)
systemctl --user enable --now podman.socket
# Verify the socket exists
ls $XDG_RUNTIME_DIR/podman/podman.sock
# Tell VSCode where the socket is
# Add to settings.json:
# "docker.host": "unix:///run/user/1000/podman/podman.sock"Replace
1000with your actual UID (id -u).
Option A — plain container with a shell and sleep loop so it stays alive:
podman run -d \
--name devbox \
-v $(pwd):/workspace:Z \
-w /workspace \
ubuntu:24.04 \
sleep infinityOption B — use a pre-built devcontainer image:
podman run -d \
--name devbox \
-v $(pwd):/workspace:Z \
-w /workspace \
mcr.microsoft.com/devcontainers/base:ubuntu \
sleep infinity- Open the Command Palette (
Ctrl+Shift+P) - Run Dev Containers: Attach to Running Container…
- Select
devboxfrom the list
A new VSCode window opens with the filesystem rooted inside the container.
Extensions installed in that window live inside the container.
Add .devcontainer/devcontainer.json to your project for a reproducible setup:
{
"name": "My Dev Container",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"runArgs": ["--userns=keep-id"],
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached"
],
"workspaceFolder": "/workspace",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"esbenp.prettier-vscode"
]
}
},
"postCreateCommand": "echo 'Container ready!'"
}Then open the project folder in VSCode and run:
Dev Containers: Reopen in Container (Ctrl+Shift+P)
--userns=keep-idmaps your host UID into the container so files created inside are owned by you on the host — important for rootless Podman.
While attached to the container, use the Ports panel (bottom bar) to forward ports, or add "forwardPorts": [3000, 8080] to devcontainer.json.
podman pull <img> Download image
podman build -t <tag> . Build image
podman run -it <img> bash Interactive container
podman run -d <img> Detached container
podman ps / ps -a List running / all
podman exec -it <n> bash Shell into running container
podman stop / rm <n> Stop / remove container
podman logs -f <n> Tail logs
podman volume create <v> Create volume
podman volume ls List volumes
podman network create <n> Create network
podman system prune -a Clean up everything
podman compose up -d Start all services (detached)
podman compose up -d --build Start and rebuild images
podman compose down Stop and remove containers
podman compose down -v Also remove named volumes
podman compose logs -f Tail all service logs
podman compose exec <svc> bash Shell into a service
podman compose ps List compose-managed containers