How to build and run rootless containers
Rootless containers reduce risk by running the app as a non-root user and the engine without root via user namespaces. Add a non-root user, fix ownership, force a UID, and enforce runAsNonRoot in Kubernetes.
Building and running rootless containers
Many containers run as root by default, so a container escape can mean host root. Rootless containers reduce this risk in two ways: building images that run as a non-root user, and running the container engine itself without root using user namespaces (Docker rootless mode or Podman).
Prerequisites
- A Linux host with user-namespace support.
- Docker or Podman installed.
Steps
1. Understand rootless containers
There are two layers: the process inside the container (the app user) and the daemon running containers (the engine). Hardening both gives the strongest posture.
2. Add a non-root user in the image
FROM alpine:3.20
RUN adduser -D -u 10001 appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
USER appuser
ENTRYPOINT ["/app/server"]
3. Fix file ownership and permissions
Ensure directories the app writes to are owned by the non-root user. Avoid binding to ports below 1024, which require extra privilege; use port 8080 instead of 80.
4. Run with a non-root UID
Even without USER in the image, you can force a UID:
docker run --user 10001:10001 myapp
5. Set up rootless mode
With Podman, commands run rootless by default. With Docker, use the rootless installation so the daemon runs as your user inside a user namespace, mapping container root to an unprivileged host UID.
6. Enforce non-root in Kubernetes
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
Kubernetes refuses to start the pod if the image would run as root.
Verification
Run the container and check the user: docker run --rm myapp id should show a non-zero UID. With rootless Docker or Podman, docker info or podman info reports rootless mode. In Kubernetes, a pod with runAsNonRoot: true should fail if the image runs as root.
Next Steps
Combine non-root execution with a read-only root filesystem, dropped capabilities, and a seccomp profile. Enforce these defaults cluster-wide with Pod Security Standards or a policy engine.
Prerequisites
- A Linux host
- Docker or Podman installed
- A Dockerfile to adjust
Steps
- 1Understand rootless containers
- 2Add a non-root user in the image
- 3Fix file ownership and permissions
- 4Run with a non-root UID
- 5Set up rootless mode
- 6Enforce non-root in Kubernetes