An Alpine base
adduser rather than useradd, and the USER line is numeric
FROM node:20-alpine WORKDIR /app
Add a non-root user properly. The two things that make it work are the numeric form and getting the ownership right at COPY time.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Generate. Nothing leaves this tab.
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.
adduser rather than useradd, and the USER line is numeric
FROM node:20-alpine WORKDIR /app
Already has a nonroot user, and there is no shell to create one with
FROM gcr.io/distroless/static
Worth checking it is the last one, and after everything that writes to system paths
FROM debian:12 USER app RUN apt-get install -y curl
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
Kubernetes needs a number to enforce runAsNonRoot and rejects the pod when it sees a name. The name also fails on a base with no /etc/passwd.
Instead:Use the numeric form. Create the named user as well if it helps readability.
They are owned by root, so the application cannot write where it needs to and fails at run time in a container that built cleanly.
Instead:Use COPY --chown=UID:GID rather than a RUN chown, which duplicates every file into a new layer.
Everything below it runs as that user, so the package installs fail on permissions.
Instead:Put it after the installs and the copies, near the end.
It has to see a number to know the user is not root, so a Dockerfile ending USER appuser is rejected by a pod with runAsNonRoot set, even though the user is not root.
A name has to be resolved from /etc/passwd inside the image, which does not exist on a scratch or distroless base. A UID needs nothing. Keep the named user for the humans reading the file and write the number in the USER line.
COPY writes as root, so switching user afterwards leaves the application unable to write to a cache directory, a socket path or a lock file. Use COPY --chown, which costs nothing, rather than a RUN chown afterwards, which duplicates every copied file into a new layer.
Everything below it runs as that user, including package installs, so a USER placed early makes the build fail on permissions. After the installs and the copies is almost always right.
So an image that listened on 80 as root fails to start. Publishing still works, because the host side is bound by the daemon: change the application to 8080 and publish it as -p 80:8080.
A bind mount keeps the host's ownership, so a container running as 10001 writing to a directory owned by 1000 gets permission denied. A named volume is different: Docker copies the image's ownership into it on first use, which is why the same setup works with a volume and not with a bind mount.
There is no shell, no adduser and no /etc/passwd to edit, so creating a user is neither possible nor needed. The :nonroot tag sets it; on the plain tag, USER 65532:65532 does the same.