Linux / shell

Container has runAsNonRoot and image will run as root

Written and reviewed by Sahil Srivastav

KubernetesSecurity contextImage user
Error: container has runAsNonRoot and image will run as root

What this error actually means

The effective security context requires a non-root process, but the container’s selected user is root. Kubelet refuses to start it. The requirement is a validation rule: runAsNonRoot does not choose a user for an image or rewrite file permissions so an arbitrary UID can run the application.

Determine the effective user from the pod and container security contexts together with the image configuration. A container-level runAsUser can override a pod-level value; absent an explicit override, an image without USER normally starts as UID zero. Looking only at one YAML section can therefore miss the setting actually selected.

A related error says the image has a non-numeric user and kubelet cannot verify that it is non-root. USER app may resolve correctly inside the image, but a name alone does not provide the numeric evidence required by this validation path. An explicit non-zero UID removes that ambiguity while still requiring you to make the image work under that identity.

Causes, most common first

  1. 1The final image has no non-root USER. Creating a user during the build is not the same as selecting it. Multi-stage builds can set USER in the builder stage while the final runtime stage inherits root again. Inspect the final image configuration, not just the first matching line in the Dockerfile.
  2. 2A security-context override selects UID zero. A chart default, container-level field or admission mutation can override the intended pod-level user. The live pod is the authoritative evidence for admitted settings; a local values file may not reflect all overlays.
  3. 3The image user is expressed only as a name. Kubelet may be unable to establish non-root status from a non-numeric image user. The name itself is not proof of UID zero. Supply a compatible numeric identity rather than disabling the check for every image with a named account.
  4. 4The image assumes root for runtime setup. An entrypoint tries to chown files, install packages or bind a privileged port before dropping privileges. Requiring non-root from process creation exposes that design. Changing the UID alone cannot grant the setup actions permissions they no longer have.

When you see it

  • The image pulls successfully but the container never produces application logs
  • The same image runs locally as root and fails under the cluster security context
  • A base-image change removes or changes the final stage’s USER instruction
  • After setting a non-root UID, startup advances but exposes file permission errors

How to diagnose it

Step 1

Read the full validation event

Replace demo and app-pod with the workload identity. Distinguish the explicit root-user error from non-numeric-user verification and from admission-policy rejection before pod creation. They happen at different stages and call for different configuration evidence.

kubectl -n demo describe pod app-pod

Step 2

Compare pod-level and container-level settings

Read every affected container, including init containers where relevant. The container-level security context can override overlapping pod-level fields, so a valid-looking pod default does not settle the effective user.

kubectl -n demo get pod app-pod -o jsonpath='{.spec.securityContext}{"\n"}{range .spec.containers[*]}{.name}{"\t"}{.securityContext}{"\n"}{end}'

Step 3

Inspect the exact final image user

Use the image digest deployed by the workload. On a machine where that image is already available, this reads configuration without starting its entrypoint. An empty User field normally means the image has not selected a non-root default.

docker image inspect registry.example.com/team/app:release-42 --format '{{json .Config.User}}'

Step 4

Validate the non-root runtime contract in staging

Run the image with the intended UID, group, mounts and security settings. Exercise startup, temporary files, logs and shutdown. Verify ownership of only the paths that must be writable; a startup success without exercising file creation can miss the next permission failure.

The fix

Choose a stable non-zero numeric UID and compatible GID, then set the image USER or the pod’s runAsUser and runAsGroup explicitly. Keep runAsNonRoot enabled so future image changes cannot silently restore root. Where a platform assigns arbitrary UIDs, design permissions for that platform’s supported group model instead of hard-coding an incompatible identity.

Create required writable directories and assign their ownership during the image build. Keep application binaries and configuration read-only where possible, and direct runtime writes to intentional paths. A blanket chmod 777 or chown of the entire image is a poor substitute for identifying those paths.

Handle mounted-volume permissions separately from image ownership. A volume hides the ownership of the directory underneath its mount point. Depending on the storage driver, fsGroup or a provisioned volume identity can provide access, but it is not a universal recursive ownership fix for every filesystem.

Move package installation and other privileged setup to the image build. Prefer an unprivileged application port with a Service mapping when the application previously depended on a low port. Test the resulting deployment under its real security constraints before promoting it.

# Pod spec fragment; the image and mounted paths must support this UID.
securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  runAsGroup: 10001
containers:
  - name: app
    image: registry.example.com/team/app:release-42
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]

How to stop it coming back

  • Inspect the USER of the final image stage in CI, including base-image updates
  • Test under the same numeric identity, mounts and security context used in production
  • Document every required writable path and its ownership contract
  • Keep user selection separate from filesystem and network capability decisions in reviews

Practise production debugging in a real repository

Reading about a failure and reproducing one are different skills. Gronex ships broken backend repositories with failing test suites that encode the real invariant, so you debug from evidence instead of memorising symptoms.

FAQ

Does runAsNonRoot automatically pick a non-root account?

No. It enforces a requirement on the selected identity. Provide a suitable image USER or runAsUser and make the application’s writable paths compatible with it.

Why does USER app still fail verification?

The runtime may receive a name without a numeric UID it can use to prove the non-root condition. A numeric USER or runAsUser resolves that validation ambiguity; ensure it matches the ownership and account assumptions inside the image.

Should I disable runAsNonRoot temporarily?

That bypasses the failed contract instead of making the image compatible with it. Fix the identity and permissions together. If a workload truly requires privileged setup, treat that requirement explicitly rather than silently relaxing all replicas.

Related

Other errors engineers hit next to this one

Full error and symptom index →