Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Podman Build: Rootless Images Without a Daemon

Sean

Platform Writer

Aug 30, 2026
8 min read

podman build takes the same Dockerfile and produces the same OCI image, without a root daemon. The trade is that rootless changes how user IDs and volume permissions behave, which is where the surprises live.

Podman Build: Rootless Images Without a Daemon

Podman’s pitch is straightforward: same commands, same Dockerfiles, same images, no daemon running as root. alias docker=podman genuinely works for most everyday use.

The interesting part is not the compatibility — it is what rootless actually means in practice, because that is where behaviour diverges from Docker in ways that matter.

Table of contents

The basics are identical

podman build -t myapp:1.0 .
podman build -t myapp:1.0 -f Dockerfile.prod .
podman build --build-arg VERSION=1.0 -t myapp:1.0 .
podman build --no-cache -t myapp:1.0 .
podman build --platform linux/arm64 -t myapp:1.0 .

Same flags, same Dockerfile syntax, same OCI output. An image built with Podman runs under Docker and vice versa, and pushes to any standard registry.

Podman also accepts Containerfile as a filename, which is the vendor-neutral name for the same thing. Dockerfile still works, and there is no reason to rename anything.

Under the hood, podman build delegates to Buildah, which is the actual build engine and is also usable standalone. That layering matters mainly because Buildah alone is a smaller thing to run in a CI container than a full Podman installation.

What daemonless and rootless actually change

No daemon. Docker’s CLI talks to dockerd, a long-running process typically running as root. Podman forks the container process directly as a child of your shell.

The consequences are practical rather than philosophical:

  • No single point of failure that takes down every container when it restarts.
  • Containers are ordinary processes, so ps, top and the OOM killer see them normally.
  • Systemd integration is natural — podman generate systemd produces a unit file, and containers become services the OS manages.
  • No socket to expose. /var/run/docker.sock mounted into a container is effectively root on the host, and there is no equivalent to leak.

Rootless. Containers run as your user, using user namespaces to map container UIDs to a range of unprivileged host UIDs. A process that thinks it is root inside the container is your unprivileged user outside it.

This is the real security argument. A container escape from a rootless Podman container lands on an unprivileged user, not on root.

The UID mapping surprise

Rootless mode is where behaviour genuinely differs from Docker, and it shows up as file ownership confusion.

Your user gets a range of subordinate UIDs, defined in /etc/subuid:

cat /etc/subuid
# alice:100000:65536

Container UID 0 maps to your host UID; container UID 1 maps to 100000, and so on. So a file written as root inside the container is owned by you on the host, and a file written as UID 1000 inside the container is owned by host UID 100999.

That produces the classic symptom: files created in a bind-mounted directory that you cannot delete without sudo. The fix is the :U mount option, which chowns the volume content to match the mapping:

podman run -v ./data:/data:U myapp
podman run --userns=keep-id -v ./data:/data myapp  # map your UID directly

--userns=keep-id is often what you actually want for development: your host UID maps to the same UID inside the container, so files come out owned by you. Also note that rootless containers cannot bind ports below 1024 by default — map to 8080 and put a proxy in front, or adjust net.ipv4.ip_unprivileged_port_start.

Building images without ever being root

Rootless building is Podman’s clearest advantage, because building images in CI normally requires either a privileged container or mounting the Docker socket — both of which hand out effective root.

Podman builds inside an unprivileged container:

# CI job
build:
  image: quay.io/podman/stable
  script:
    - podman build -t $REGISTRY/myapp:$CI_COMMIT_SHA .
    - podman push $REGISTRY/myapp:$CI_COMMIT_SHA

For genuinely constrained environments, Buildah alone is lighter still and supports building without a Dockerfile at all — useful when the build is generated rather than authored:

ctr=$(buildah from alpine:3.20)
buildah run $ctr -- apk add --no-cache curl
buildah config --entrypoint '["/usr/bin/curl"]' $ctr
buildah commit $ctr mycurl:1.0

That imperative style is occasionally exactly what you want, though a Dockerfile remains the right default because it is declarative and cacheable.

Pods, and where Compose fits

Podman’s name comes from pods — groups of containers sharing a network namespace, the same concept Kubernetes uses:

podman pod create --name web -p 8080:8080
podman run -d --pod web --name api myapp:1.0
podman run -d --pod web --name cache redis:7
# api reaches cache on localhost:6379

podman generate kube web > web.yaml   # export as a Kubernetes manifest

That last command is genuinely useful for developing locally in a shape that resembles a cluster deployment.

For Compose files, podman-compose exists and covers common cases, and Podman can expose a Docker-compatible socket that the real docker compose will talk to. Neither is quite as seamless as Compose on Docker, and it is the most common friction point when migrating.

The honest summary on migration: single containers and builds move over with no effort. Compose-heavy setups need testing. Anything mounting the Docker socket needs rethinking, which is arguably the point.

What the registry is for

Whichever tool builds it, the output is an OCI image, and the deployment question is the same: build somewhere, push to a registry, run somewhere else.

That indirection is what makes local tooling a preference rather than a constraint. Build with Podman on your laptop for the rootless safety, and the image that comes out is the same artifact any runtime can pull.

On RunxBuild, Docker services build from a connected repository rather than from a locally pushed image — push the code, get a build log, a live route, runtime logs and rollback to the previous deploy. Your local choice of Podman or Docker for iteration stays a local choice, since the Dockerfile is the shared contract.

How this fits the rest of the stack

podman build is a drop-in for docker build that removes the root daemon, and the security argument for that is real. Budget some time for the rootless UID mapping — :U and --userns=keep-id are the two flags that resolve most of it — and expect Compose to be the rough edge. Since the output is a standard OCI image either way, the build tool stays a local preference; the RunxBuild hosting calculator shows what running the resulting service costs.

Useful related references:

FAQ

Is podman build compatible with Dockerfiles?

Yes. Podman uses the same Dockerfile syntax and flags, and produces standard OCI images that run under any container runtime. It also accepts Containerfile as a filename, but Dockerfile works unchanged.

What does rootless mean in Podman?

Containers run as your unprivileged user, with user namespaces mapping container UIDs to a range of host UIDs from /etc/subuid. A process that is root inside the container is your ordinary user outside it, so a container escape does not land on host root.

Why can I not delete files created by a Podman container?

Rootless UID mapping means files written inside the container are owned by a mapped subordinate UID on the host, not by you. Use the :U mount option to chown the volume, or --userns=keep-id so your host UID maps to the same UID inside.

Can Podman run docker-compose files?

Mostly. podman-compose handles common cases, and Podman can expose a Docker-compatible socket for the real docker compose to use. This is the roughest part of a migration — single containers and builds move over with no changes.

Why can rootless Podman not bind port 80?

Unprivileged users cannot bind ports below 1024. Map to a high port such as 8080 and put a reverse proxy in front, or lower net.ipv4.ip_unprivileged_port_start on the host if you control it.

#podman build#rootless containers#Buildah#container images#Docker alternative