Fix 'container has runAsNonRoot and image will run as root' in Kubernetes SecurityContext

intermediate☸️ Kubernetes2026-05-31| Kubernetes 1.18+, any cloud provider (EKS, GKE, AKS) or on-premise cluster with PodSecurity admission or OPA/Gatekeeper enforcing non-root policy

Error Message

Error: container has runAsNonRoot and image will run as root (pod: "...", container: "...")
#kubernetes#security-context#runAsNonRoot#pod-security#container

The Error

You applied a manifest with runAsNonRoot: true in the securityContext, and Kubernetes rejected the pod on the spot:

Error: container has runAsNonRoot and image will run as root (pod: "my-app-7d9f8b-xkp2q", container: "app")

The pod never starts. kubectl get pods shows it stuck in Error or frozen at Pending. This is a hard kubelet rejection β€” it doesn't even attempt to pull or run the container. No partial execution, no logs, just a dead pod.

Why This Happens

When you set runAsNonRoot: true, Kubernetes checks the UID the container would run as. If the image's USER directive is missing or set to root (UID 0), and you haven't specified runAsUser in the securityContext, the kubelet blocks it. The check fires before the container starts β€” clean fail, every time.

Three situations typically cause this:

  • Using an upstream image (nginx, redis, python, node) that defaults to root
  • A cluster-wide PodSecurity policy or OPA/Gatekeeper rule that injects runAsNonRoot: true automatically
  • You added runAsNonRoot: true to tighten security but forgot to pair it with runAsUser

Fix 1: Add runAsUser to the SecurityContext (Fastest)

Tell Kubernetes which non-root UID to use. Pick any UID above 0 β€” 1000 or 65534 (nobody) are conventional choices:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000        # ← add this
        runAsGroup: 1000       # optional but recommended
        fsGroup: 1000          # optional: for volume ownership
      containers:
      - name: app
        image: nginx:latest
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000      # can also set per-container
          allowPrivilegeEscalation: false

Apply and watch:

kubectl apply -f deployment.yaml
kubectl get pods -w

Caveat: Some images β€” including the official nginx β€” bind to port 80 internally, which requires root. Force a non-root UID and the container fails at startup with a permission error, not a security rejection. Use an image built for non-root instead: nginxinc/nginx-unprivileged runs on port 8080 as UID 101 and works with this securityContext out of the box.

Fix 2: Check What UID the Image Actually Uses

Before committing to a UID, find out what the image expects. Run these two commands:

# Run the image locally and check the effective user
docker run --rm nginx:latest id
# uid=0(root) gid=0(root) groups=0(root)

# Inspect the image manifest
docker inspect nginx:latest | grep -i user
# "User": ""
# Empty = defaults to root

Already has a non-root user? Find its UID and use that:

docker run --rm your-image:tag id
# uid=1001(appuser) gid=1001(appuser)

# Then in your securityContext:
#   runAsUser: 1001

Fix 3: Rebuild the Image With a Non-Root User

Bake the user into the image and you never have to deal with this again. Every cluster that runs the image gets the right UID automatically, with no securityContext patching required:

FROM node:18-slim

# Create a non-root user
RUN groupadd --gid 1001 appgroup && \
    useradd --uid 1001 --gid appgroup --shell /bin/bash appuser

WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production

USER appuser          # ← this is what Kubernetes reads

CMD ["node", "server.js"]

Build, push, and update your manifest:

docker build -t your-registry/my-app:v2 .
docker push your-registry/my-app:v2

Update the image tag in your manifest and apply. No runAsUser needed in the securityContext β€” the image already declares it.

Verify the Fix

Check the pod status, then confirm the UID from inside the container:

# Pod should be Running, not Error/Pending
kubectl get pods

# Describe to confirm no security errors
kubectl describe pod my-app-xxx

# Exec in and verify the user
kubectl exec -it my-app-xxx -- id
# Expected: uid=1000 gid=1000 groups=1000

# Quick alternative:
kubectl exec -it my-app-xxx -- whoami

Running pod, non-zero UID β€” you're done.

If the Error Comes From a Cluster Policy

You'll sometimes hit this error without ever writing runAsNonRoot: true yourself. That means the cluster is enforcing it for you β€” usually via PodSecurity admission or Gatekeeper.

# Check if namespace has a PodSecurity label
kubectl get namespace my-namespace -o yaml | grep pod-security

# Example output:
# pod-security.kubernetes.io/enforce: restricted

The restricted PodSecurity standard mandates runAsNonRoot: true on every pod in that namespace. The fix is identical β€” add runAsUser or rebuild the image. You just need to know the constraint is coming from the cluster, not your manifest.

Quick Reference

  • Fastest fix: Add runAsUser: 1000 alongside runAsNonRoot: true
  • Port 80/443 images: Switch to an unprivileged variant (e.g., nginxinc/nginx-unprivileged on port 8080, UID 101)
  • Custom images: Add USER 1001 to your Dockerfile
  • Which UID to use: Anything above 0 β€” 1000, 1001, and 65534 are conventional
  • Don't: Remove runAsNonRoot: true just to silence the error β€” it exists for a reason

Related Error Notes