Introduction
Podman ("Pod Manager"):
- 100% Docker CLI compatible (
alias docker=podman) - No daemon: each command runs as a user process
- Rootless by default
- Native systemd unit generation
- Supports pods (group of containers sharing resources, like Kubernetes)
- Standard OCI format
Advantages vs Docker:
- Security: no root socket exposed
- No SPOF (no daemon)
- Native systemd integration
Prerequisites
- Linux VPS Debian 12 / Ubuntu 24.04 / Rocky 9
- Root access to install
- A non-root user for rootless usage
Step 1: Installation
Debian / Ubuntu
sudo apt update
sudo apt install -y podman
podman --version
Rocky / RHEL
sudo dnf install -y podman
Step 2: First container (rootless)
As normal user (not root):
podman run -d -p 8080:80 --name web nginx
podman ps
curl http://localhost:8080
Container runs as your user. No daemon, no root.
Step 3: Docker-compatible commands
podman ps
podman images
podman pull alpine
podman exec -it web bash
podman logs web
podman stop web
podman rm web
podman rmi nginx
podman inspect web
Step 4: docker = podman alias
For transparent transition:
sudo apt install -y podman-docker
/usr/bin/docker is created as symlink to podman.
Step 5: Build an image
Dockerfile (or Containerfile):
FROM alpine:3.18
RUN apk add --no-cache nginx
COPY index.html /var/www/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
podman build -t mysite:1.0 .
podman run -d -p 8080:80 mysite:1.0
Step 6: Generate systemd service
Instead of a daemon to restart containers at boot, Podman generates systemd services directly:
podman create --name myapp -p 8080:80 nginx
podman generate systemd --new --files --name myapp
Generates ~/container-myapp.service. Install:
mkdir -p ~/.config/systemd/user
mv container-myapp.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now container-myapp
systemctl --user status container-myapp
To run service after logout:
loginctl enable-linger $USER
Step 7: Quadlet (modern way)
Podman 4.4+: Quadlet simplifies systemd unit creation. Create ~/.config/containers/systemd/myapp.container:
[Unit]
Description=My Nginx app
After=network-online.target
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
ContainerName=myapp
[Service]
Restart=on-failure
[Install]
WantedBy=multi-user.target default.target
systemctl --user daemon-reload
systemctl --user start myapp
Quadlet generates the systemd service on the fly.
Step 8: Pods (container groups)
A Podman pod = multiple containers sharing network + storage:
podman pod create --name webpod -p 8080:80
podman run -d --pod webpod --name nginx nginx
podman run -d --pod webpod --name redis redis
nginx and redis share localhost. nginx reaches redis at localhost:6379.
Step 9: podman-compose
For docker-compose.yml compatibility:
sudo apt install -y podman-compose
podman-compose up -d
podman-compose ps
podman-compose down
Step 10: Security: SELinux / AppArmor
Podman natively integrates with SELinux. If SELinux is enforcing, add :Z to volumes:
podman run -d -v /home/user/data:/data:Z nginx
Step 11: Networking
podman network create mynet
podman run -d --network=mynet --name web nginx
podman run -d --network=mynet --name app myapp:1.0
podman exec app curl http://web
Step 12: Export / Import to Kubernetes
podman generate kube myapp > myapp-k8s.yaml
kubectl apply -f myapp-k8s.yaml
Reverse:
podman play kube myapp-k8s.yaml
Podman launches containers as described in K8s YAML. Useful for local dev without K8s.
Troubleshooting
"newuidmap: write to uid_map failed"
Rootless limits misconfigured:
cat /etc/subuid /etc/subgid
Should have a range:
youruser:100000:65536
Else:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 youruser
podman system migrate
Port < 1024 in rootless
Rootless can't bind privileged ports. Solutions:
- Use port >= 1024 then reverse-proxy
- Allow your user:
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/podman
- Enable
net.ipv4.ip_unprivileged_port_start=80:
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system
"Error: error preparing container"
Usually SELinux or permissions:
podman info
podman events
Image won't pull
Configure registries in ~/.config/containers/registries.conf:
[[registry]]
location = "docker.io"
Useful commands
podman run -d -p 8080:80 nginx
podman ps -a
podman stop myapp
podman rm myapp
podman exec -it myapp sh
podman pull alpine
podman build -t myapp:1.0 .
podman tag myapp:1.0 registry/myapp:1.0
podman push registry/myapp:1.0
podman images
podman system info
podman system prune -a
podman version
podman pod ps
podman pod create --name webpod -p 8080:80
podman pod start webpod
podman generate systemd --new --files --name myapp
podman generate kube myapp > k8s.yaml
podman play kube k8s.yaml
Conclusion
Podman replaces Docker with advantages:
- No daemon = no SPOF
- Rootless = better security
- Native systemd integration
- Kubernetes YAML compatible
Going further:
- Migrate your
docker-composestack with podman-compose or Quadlet - Combine with Buildah for daemon-less builds
- Explore Podman Desktop (cross-platform GUI)
Resources
- Official docs: https://docs.podman.io
- Site: https://podman.io
- Quadlet: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html
- GitHub: https://github.com/containers/podman

















