Podman is a daemonless container engine that’s fully compatible with Docker images and containers.
Let’s see Podman in action. Suppose you have a simple Dockerfile for a Node.js app:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
To build this with Docker, you’d run docker build -t my-node-app .. With Podman, the command is identical: podman build -t my-node-app ..
Now, to run it: docker run -p 3000:3000 my-node-app. Podman uses podman run -p 3000:3000 my-node-app.
The core difference is that Podman doesn’t run a persistent background daemon. Instead, it uses a fork/exec model. When you run a podman command, it directly interacts with the Linux kernel’s container primitives (namespaces, cgroups) to create and manage containers. This means there’s no single point of failure like the Docker daemon, and it can run rootless by default, enhancing security.
Here’s how you might manage a multi-container application using Podman Compose, which is a drop-in replacement for Docker Compose. Your docker-compose.yml would look like this:
version: '3.8'
services:
web:
image: my-node-app
ports:
- "3000:3000"
volumes:
- .:/app
You’d manage this with docker-compose up -d. With Podman, you’d use podman-compose up -d. The podman-compose tool translates these commands into Podman’s native commands.
The mental model for Podman is that it’s a direct user-space tool for interacting with the OS’s containerization features. Think of it as docker but without the d (for daemon). This means commands like docker ps become podman ps, docker images become podman images, and so on. You can even import your existing Docker images: podman load -i my-docker-image.tar.
Podman’s rootless mode is a significant advantage. When running rootless, containers are created with a user namespace, mapping the container’s root user (UID 0) to a non-privileged user on the host. This dramatically reduces the attack surface. If a container is compromised, the attacker is confined to the privileges of the unprivileged user, not root on the host. To enable this, you typically need to configure subuid and subgid for the user running Podman. On most modern Linux distributions, this is handled by logind or systemd-user-sessions, and you can check your mappings with id -u <username>.
The key to Podman’s flexibility lies in its command-line interface, which mirrors Docker’s so closely that most workflows are a direct substitution. However, understanding that it’s not interacting with a central daemon is crucial. Each Podman process is responsible for its own container lifecycle. This allows for more granular control and avoids the resource overhead of a constantly running daemon.
When you use podman run --user 1000:1000 ..., Podman respects that user ID within the container, and if running rootless, it maps that to the appropriate sub-UID range for the host user. This ensures that applications inside the container run with the intended user permissions, which is vital for file ownership and process execution.
The next concept to explore is Podman’s integration with systemd for managing container lifecycles as services.