how to use a non-root user in Dockerfiles
Shows how to run containers as a non-root user in Dockerfiles so a container escape does not hand over root. Use this when writing or reviewing Dockerfiles and you want the smallest possible privilege for the app process. Not for fixing file-permission errors by running everything as root again.
TL;DR
Add a non-root user in your Dockerfile and switch to it with USER before the app starts. Create the user in an early layer, fix ownership of the app files, and keep root-only steps before the switch. A container breakout from a non-root process is a much smaller problem than one from root.
The query
how to use a non-root user in DockerfilesUse this when
- You are writing a new Dockerfile and the app currently runs as root by default
- You are hardening existing images and want to drop privileges for the runtime process
- A scan flags your images for running as root
- You need your images to run on platforms that enforce non-root policies
Not for
- Debugging permission errors by adding USER root back everywhere; fix the file ownership instead
- Changing the user your host daemon runs as; this is the in-container process user
- Multi-tenant isolation on its own; pair it with namespaces and seccomp for real boundaries
- Secrets handling; non-root does not hide env vars or mounted secrets from the process
Steps
- Create a dedicated user in the Dockerfile. Use a distro-appropriate command (useradd, adduser) with no login shell and no password, and give it a fixed UID so file ownership is predictable. Expected output: the user exists in the image, verifiable with
docker run --rm [image] id [username]. - Set ownership on the app files. Run
chown -R [username]:[group] /app(or your app dir) before switching users so the app can read its own files. Expected output:docker run --rm [image] ls -l /appshows the user owning the files. - Switch users near the end of the Dockerfile. Put
USER [username]after all root-needed steps (package installs, directory setup) and before CMD or ENTRYPOINT. Expected output: the Dockerfile has USER as one of the last instructions. - Build and verify the running user. Build the image, run it, and check that the app process is not UID 0 with
docker exec [container] id. Expected output: uid is your app user, not 0. - Test the app fully as that user. Writability, port binding (use ports above 1024), and log paths all need to work without root. Expected output: the app starts, serves traffic, and writes logs with no permission errors.
- Enforce it in CI. Add a check that fails the build if the image's default user is root. Expected output: a pipeline gate that blocks root-by-default images from shipping.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_TyYiU4JrCxGkphpXvZnA
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.