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: trueautomatically - You added
runAsNonRoot: trueto tighten security but forgot to pair it withrunAsUser
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: 1000alongsiderunAsNonRoot: true - Port 80/443 images: Switch to an unprivileged variant (e.g.,
nginxinc/nginx-unprivilegedon port 8080, UID 101) - Custom images: Add
USER 1001to your Dockerfile - Which UID to use: Anything above 0 β 1000, 1001, and 65534 are conventional
- Don't: Remove
runAsNonRoot: truejust to silence the error β it exists for a reason

