Podman: Running Containers Without Docker (and Without Losing Your Mind)
A daemonless, rootless container engine that mostly speaks Docker

For about a decade, “containers” and “Docker” were synonyms in most people’s heads, mine included. You installed Docker, you ran docker run, a daemon somewhere did the work, and you didn’t think too hard about the fact that the daemon ran as root and you were talking to it through a socket that was, functionally, a key to the whole machine. Podman is what you get when someone looks at that arrangement and asks why it has to be like that. The answer, it turns out, is that it doesn’t.
I switched my personal boxes over a while back and have very few regrets. Most of the time it’s a drop-in. Some of the time it isn’t, and being honest about which times is the useful part of this post.
1 No daemon, and why that matters
Docker runs a long-lived root daemon (dockerd); your CLI is just a client poking it. Podman has no daemon. When you run podman run, the container is a direct child of your shell’s process tree, managed by a small monitor called conmon. That sounds like an implementation detail until you realise the consequences: there’s no single privileged process that, if compromised, owns the host. There’s no socket that grants root to anyone in the docker group. And containers integrate cleanly with systemd, because they are just processes.
The CLI is deliberately Docker-compatible. Almost everything you know transfers:
podman run -d --name caddy -p 8080:80 docker.io/library/caddy:2
podman ps
podman logs caddy
podman exec -it caddy sh
# If muscle memory insists on "docker", alias it and move on:
alias docker=podman2 Rootless by default, and the one gotcha
The headline feature is rootless containers. You run as your own unprivileged user, and Podman uses user namespaces to map your UID to “root” inside the container while you remain a nobody on the host. A container breakout lands the attacker as your unprivileged user, not as host root. For anything self-hosted and internet-adjacent, that’s a meaningful reduction in blast radius.
The gotcha that catches everyone: privileged ports. As a normal user you can’t bind below 1024, so -p 80:80 fails. You either publish to a high port and reverse-proxy, lower the net.ipv4.ip_unprivileged_port_start sysctl, or grant the capability. None of these is hard; all of them surprise you the first time.
# Let rootless containers bind low ports system-wide:
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/podman.conf
sudo sysctl --system3 Pods, and where the name comes from
Podman can group containers into a pod — the same concept Kubernetes uses, a set of containers sharing a network namespace and localhost. That’s not a coincidence; the kinship with Kubernetes is intentional. You can even export a running pod to a Kubernetes YAML manifest with podman generate kube, which is a genuinely lovely way to prototype on a laptop and then ship the same shape to a real cluster.
4 Compose and quadlets
The most common objection is “but my whole stack is a docker-compose.yml.” Fine. podman-compose exists, and there’s a Docker-API-compatible socket you can point the real docker compose at if you must. But the idiomatic Podman answer is quadlets: you describe a container in a small systemd unit file, drop it in the right place, and systemd manages it like any other service — start on boot, restart on failure, journald logging, all for free.
# ~/.config/containers/systemd/caddy.container
[Container]
Image=docker.io/library/caddy:2
PublishPort=8080:80
Volume=%h/caddy/Caddyfile:/etc/caddy/Caddyfile:ro,Z
[Service]
Restart=always
[Install]
WantedBy=default.targetRun systemctl --user daemon-reload and systemctl --user start caddy, and your container is now a first-class systemd service. No daemon babysitting a restart policy — the init system you already trust does it.
5 Where it bites
It’s not pure sunshine. Rootless networking historically went through a userspace proxy (slirp4netns) that was slower than the kernel path Docker uses; the newer pasta backend closes most of that gap, but if you’ve got throughput-sensitive workloads, test before you assume parity. A handful of images and tools bake in assumptions about the Docker socket’s exact location or behaviour, and those need a nudge. And rootless storage lives under your home directory using overlay-on-fuse in some configurations, which can be slower than native overlayfs. For most self-hosters, none of this registers. For someone running a CI fleet, it might.
6 Is it worth it?
For a personal server, a homelab, or anywhere you’d rather not run a root daemon that’s a soft target — yes, unreservedly. The security model is better, the systemd integration is genuinely pleasant, and the CLI is close enough to Docker that the switch costs you an afternoon, not a weekend. If you’re deep into a Docker-Compose-and-Swarm shop with tooling that assumes the daemon, the migration is more involved and you should weigh it. But “containers without Docker” is no longer the awkward proposition it once sounded like. It’s just a quieter, more sensible way to run the same workloads.




