If a Dockerfile's final stage never sets USER, or its last USER is root or 0, the container's main process runs as root (UID 0). A bug in the app then gives an attacker root inside the container: they can rewrite the app's code and config, and any extra capability, mounted socket or runtime flaw becomes a shorter path to the host. A non-root numeric USER removes that head start.
openrouting — dockerfile explainer
$ explain Dockerfile! Final stage never sets USER, so it runs as root mediumline 1fix: see below$
What the free Dockerfile explainer reports on the risky example below.
Why it matters
Root in a container is still the host's UID 0 unless user namespaces are configured. Container isolation limits what it can reach, but every weakness in that isolation, such as a mounted Docker socket, an added capability or a kernel bug, is worse when the process starts as root.
Running as an unprivileged user also protects the image from the app itself: if the code is owned by root and the process runs as UID 10001, a compromised process cannot plant a backdoor in its own files.
The explainer follows multi-stage inheritance: a final stage built FROM an earlier stage takes that stage's USER. It treats a base tag containing nonroot (as in distroless :nonroot) as non-root when the final stage sets no USER.
Risky and safer Dockerfile
RiskyDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
HEALTHCHECK CMD ["node", "src/healthcheck.js"]
CMD ["node", "src/server.js"]
SaferDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
USER 10001
HEALTHCHECK CMD ["node", "src/healthcheck.js"]
CMD ["node", "src/server.js"]
How to fix it
Do every step that needs root (package installs, file copies) first, leaving the application files owned by root.
Give the runtime user write access only to the directory that needs it, e.g. RUN mkdir /app/data && chown 10001 /app/data.
End the final stage with USER 10001. A numeric UID works without a passwd entry and lets Kubernetes verify runAsNonRoot.
When it's acceptable
Containers whose job genuinely requires root, such as some system agents. Grant only the capabilities they need and document why.
Images where the orchestrator always sets a non-root user (for example Kubernetes runAsUser). Setting USER in the image still protects anyone who runs it another way.
Check your own Dockerfile
The risky example is loaded below. Paste your own file to check it for this and every other Dockerfile rule. It runs in your browser; nothing is uploaded.
Without USER the process runs as root (UID 0) in the container. A compromised app then has root to rewrite your code and, with a mounted socket, extra capability or a runtime bug, a short path to the host.
Safer: Create an unprivileged user and switch to it with a numeric UID, e.g. USER 10001, leaving the application code owned by root.
A version tag can be re-pushed to different content; only a digest is immutable. This applies to every tagged FROM in the file.
Safer: Pin the digest as well (image:tag@sha256:<digest>) and let a bot open pull requests when it changes.
Sets the default command to node src/server.js. It runs when the container starts, and anything passed to docker run replaces it.
Questions
Do Docker containers run as root by default?
Yes. Unless the image or the run command sets a user, the process runs as root (UID 0) inside the container.
Should Dockerfile USER be a name or a numeric UID?
Either works for Docker, but a numeric UID such as 10001 doesn't need an /etc/passwd entry, and Kubernetes can only verify runAsNonRoot when the image user is numeric.
Does a multi-stage build inherit USER from an earlier stage?
A stage that starts FROM an earlier stage name inherits that stage's settings, including USER. A stage that starts FROM a fresh image does not, so set USER in the final stage itself.