Skip to content
ohsudo, Home
Kubernetes

Kubernetes "container has runAsNonRoot and image will run as root": fix the image or the pod

CreateContainerConfigError with runAsNonRoot: the image runs as root or has a non-numeric USER. Fix the Dockerfile, set runAsUser, and keep restricted security.

Intermediate4 min readApplies to Kubernetes 1.25+ · kubectl · any distribution (EKS · GKE · AKS · k3s · kubeadm) · Docker / OCI images

by OhSudo ··

Not sure this is your cause?Find it step by step: Pod not running →

Part of a learning path:Kubernetes: debugging pods

The pod never starts. kubectl get pods shows a status you don’t see often:

NAME READY STATUS RESTARTS AGE
web-7c9d8b6f5d-x2k4q 0/1 CreateContainerConfigError 0 41s

and kubectl describe pod explains it in the Events at the bottom:

Warning Failed 12s (x4 over 41s) kubelet Error: container has runAsNonRoot and image will run as root (pod: "web-7c9d8b6f5d-x2k4q_shop(3f1c…)", container: web)

Your pod spec says runAsNonRoot: true — a good security setting — and the image says it runs as root. The kubelet checks the two before it starts the container, sees the conflict, and refuses. Nothing is wrong with the cluster; the image and the manifest disagree.

  1. Pod specrunAsNonRoot: true
  2. Imageruns as root (or a user name, not a number)
  3. kubeletCreateContainerConfigError(breaks here)
The kubelet compares the pod's runAsNonRoot with the image's user before starting the container, sees the conflict, and refuses.

TL;DR

Terminal window
kubectl describe pod <pod> | tail -n 5 # the exact error
docker image inspect -f '{{.Config.User}}' <image> # empty or "root" = runs as root
kubectl get pod <pod> -o jsonpath='{.spec.securityContext}' # where runAsNonRoot comes from

Then either give the image a numeric non-root user (USER 10001 in the Dockerfile), or set runAsUser: 10001 in the pod’s securityContext.

1. Check which user the image runs as

The user comes from the last USER line of the Dockerfile — or from the base image, if the Dockerfile has none:

Terminal window
docker image inspect -f '{{.Config.User}}' registry.example.com/web:1.4.2
  • Empty, root or 0: the image runs as root. Most official base images (node, python, nginx, golang) do, unless you change it.
  • A name, like node or app: see section 3 — this is the other variant of the error.
  • A number, like 10001 or 1000:1000: fine; the error must come from something else (a runAsUser: 0 in the pod spec).

Without Docker at hand, crane config <image> | jq .config.User or skopeo inspect --config docker://<image> show the same.

2. Fix the image: a numeric non-root user

The cleanest fix is in the Dockerfile — create a user and switch to it, by number:

FROM python:3.13-slim
RUN useradd --system --uid 10001 --no-create-home --shell /usr/sbin/nologin app
WORKDIR /app
COPY --from=build /opt/venv /opt/venv
COPY . .
USER 10001
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]

Official images often already have a user you can reuse: node images have node (UID 1000), nginxinc/nginx-unprivileged runs as 101, distroless images have nonroot (65532). The Dockerfile Generator writes multi-stage Dockerfiles that end with a numeric USER for exactly this reason.

Note

A non-root process can’t bind ports below 1024. If your app listens on 80, move it to 8080 (and keep port 80 on the Service: port: 80, targetPort: 8080). Files it writes need to be writable by that user — a mounted volume, or a folder you chown in the Dockerfile.

3. “image has non-numeric user (node), cannot verify user is non-root”

Error: container has runAsNonRoot and image has non-numeric user (node), cannot verify user is non-root (pod: "web-…", container: web)

The image does run as a normal user — but by name. The kubelet only sees the image config, not the image’s /etc/passwd, so it can’t know whether node is UID 1000 or UID 0. With runAsNonRoot: true it refuses rather than guess. Two fixes, either is enough:

  • In the Dockerfile, use the number: USER 1000 instead of USER node.
  • In the pod spec, tell Kubernetes the UID, so it doesn’t need to look it up:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000

4. Can’t change the image? Set runAsUser

For a third-party image that runs as root but doesn’t need to, set the user in the pod. Kubernetes then starts the process with that UID, whatever the image says:

spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001 # mounted volumes become writable for this group
containers:
- name: web
image: registry.example.com/web:1.4.2
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]

The pod now starts — but the app may fail next, with Permission denied on a file the image created for root, or bind: permission denied on port 80. Then the image really needs root for something; fix the image (section 2) rather than dropping runAsNonRoot. The Kubernetes Manifest Generator writes this whole security context, which meets the “restricted” Pod Security Standard.

5. Where runAsNonRoot comes from

If you don’t remember setting it, look for it:

Terminal window
kubectl get pod web-7c9d8b6f5d-x2k4q -o jsonpath='{.spec.securityContext}{"\n"}{.spec.containers[*].securityContext}{"\n"}'

It can come from your manifest, from a Helm chart’s default values (many charts set podSecurityContext.runAsNonRoot: true), or from a policy engine like Kyverno or Gatekeeper that adds it automatically. Removing it makes the error go away — and runs your app as root again. Prefer fixing the image.

Shell, we have a problem!

Similar, but different: pods "web-…" is forbidden: violates PodSecurity "restricted:latest": runAsNonRoot != true appears when the pod is created, in the events of the ReplicaSet (kubectl describe rs), not of the pod. That’s the namespace’s Pod Security Admission rejecting a pod that doesn’t set runAsNonRoot: true — the opposite problem: add the security context from section 4. And on OpenShift, pods run with a random UID by default, so images must work as any user, not just one specific UID.

After the fix

Terminal window
kubectl rollout restart deployment/web
kubectl rollout status deployment/web
kubectl exec deploy/web -- id # uid=10001 … — not uid=0(root)

$ ./practice.sh

Practice this in the terminal

Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.

Open the terminal →

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles