Docker & Podman¶
Almost every job posting, tutorial, and Stack Overflow answer says docker, not podman. This course
teaches Podman deliberately (daemonless, rootless by default), but you'll meet Docker everywhere else —
in interviews, in other teams' repos, in the wider ecosystem. The good news: the two are close to
interchangeable at the command line. This page gives you the mapping, so Docker-flavored instructions
translate instantly, and the one architectural difference that actually changes behavior.
TL;DR — in 30 seconds:
- Docker runs a root-owned background daemon (
dockerd) that owns every container; thedockerCLI talks to it over a socket. Podman has no daemon — eachpodmancommand talks straight to the kernel, and it runs rootless by default. - Commands are ~1:1:
docker run→podman run,docker build→podman build,docker ps→podman ps— most muscle memory transfers directly, including aDockerfile(Podman calls the same syntax aContainerfile, and reads either name). - The daemon is where the difference bites: a compromised Docker daemon socket is effectively root on the host; Podman has no such single point of privilege to compromise.
1. Same commands, different architecture¶
flowchart LR
subgraph Docker
DCLI["docker CLI"] --> DD["dockerd (daemon, root)"]
DD --> DC1["Container"]
DD --> DC2["Container"]
end
subgraph Podman
PCLI1["podman CLI"] --> PC1["Container"]
PCLI2["podman CLI"] --> PC2["Container"]
end
Docker's docker CLI is a thin client: it sends every command over a socket to dockerd, a long-running
root daemon that actually creates and manages containers. Podman's CLI is the whole client — each
invocation talks to the kernel directly (namespaces, cgroups) and exits when the command finishes; there's
no persistent process holding the keys. This is the daemonless half of Podman's model covered on the
main course page, and it's the one structural difference underneath everything else on this page.
2. The command mapping¶
The CLIs were designed to be compatible — Podman's stated goal is that alias docker=podman works for
the overwhelming majority of everyday use:
| Task | Docker | Podman |
|---|---|---|
| Run a container | docker run -d --name N image |
podman run -d --name N image |
| Build an image | docker build -t name:tag . |
podman build -t name:tag . |
| List running containers | docker ps |
podman ps |
| View logs | docker logs -f N |
podman logs -f N |
| Shell inside | docker exec -it N /bin/sh |
podman exec -it N /bin/sh |
| Stop / remove | docker stop N / docker rm N |
podman stop N / podman rm N |
| List images | docker images |
podman images |
| Compose-style multi-container | docker compose up |
podman compose up (or podman play kube) |
| Build file | Dockerfile |
Containerfile (Podman reads either name) |
The flags you already know from the cheat sheet (-p, -v, -e, --rm, --cap-drop, --user) carry
over unchanged — they're the same underlying kernel primitives (namespaces, cgroups, bind mounts) exposed
through a near-identical CLI surface.
3. Where the difference actually bites¶
Most days, the daemon is invisible. It shows up in a few concrete spots:
- The socket is a privilege boundary. Anything with access to
/var/run/docker.sockcan, in effect, run as root on the host — mountingdocker.sockinto a container (a common CI/tooling pattern) hands that container root-equivalent control of the whole machine. Podman has no equivalent always-on socket to protect by default. - Rootless is opt-in on Docker, default on Podman. Docker has a rootless mode, but it's a separate install path most teams don't use; Podman ships rootless as the default you have to opt out of. If a Docker tutorial says "just run as root," that instinct doesn't carry over — see the rootless & security page for why Podman starts from the safer default instead.
- A survives-reboot daemon vs. no daemon to manage. Docker containers can be configured to restart
when
dockerdrestarts (e.g. after a host reboot) because the daemon persists and tracks them; Podman containers are supervised per-process instead — see the Quadlet module for how Podman does the equivalent job with systemd, without ever introducing a daemon.
4. When you'll actually see Docker¶
You don't need to install Docker to use this course — Podman covers the same ground. But you'll run into
docker in the wild:
- Job postings and interviews default to saying "Docker" as the generic word for containers, even at shops that don't run it — the mapping above is what lets you answer fluently either way.
- Other teams' Dockerfiles and
docker-compose.ymlfiles — aContainerfileand aDockerfileare the same syntax; Podman builds one unmodified. - Docker Hub remains the largest public image registry regardless of which CLI pulls from it.
5. How this connects¶
- The daemonless/rootless model this page names is the same one the main course page and the rootless & security page cover in depth — this page is the "translate from what you'll see out there" companion to those, not a new architecture.
- The Containerfile syntax here is identical to the one covered on the images & builds page — nothing about layers, multi-stage builds, or the build cache changes based on which CLI reads the file.
- The docker.sock privilege-boundary point is the same "don't hand out root" concern that motivates Kubernetes' RBAC and Pod Security Standards (module 04, Kubernetes & Helm) — a daemon socket and a cluster API server are both a single point of privilege worth guarding.
Common gotchas
- Assuming
docker composeandpodman composeare identical. Fix:podman composeshells out to an external Compose implementation and mostly works, but Podman's native equivalent for multi-container YAML ispodman play kube— check the cheat sheet for both. - Mounting
docker.sockinto a container out of habit. Fix: that pattern hands the container root-equivalent host access; if you're on Podman for its rootless security model, don't reintroduce the same risk through a borrowed Docker pattern. - Expecting a
dockerd-equivalent process to inspect. Fix: there is no Podman daemon topsfor or restart — if a container needs to persist across reboots, that's a systemd/Quadlet job, not a daemon-restart job.
Check yourself — why can mounting /var/run/docker.sock into a container be dangerous in a way that has no direct Podman equivalent?
Anything with access to that socket can issue commands to the root-owned dockerd daemon, which is
effectively root access to the whole host. Podman has no persistent root daemon or socket playing that
role, so there's no single mount point that hands out equivalent host-wide privilege.
Check yourself — a coworker pastes a docker run -p 8080:80 -v data:/var/lib/app image command. What do you run instead on Podman?
The identical command with podman substituted for docker — podman run -p 8080:80 -v data:/var/lib/app image. The flags and syntax are unchanged.
You can defend this when you can translate any everyday Docker command to Podman on sight, explain
why the daemon is the one thing that's architecturally different (not just a naming difference), and
name a concrete risk (the docker.sock mount) that follows from that difference.