Enforce a Non-Root Container User
Set a numeric non-root user and group in the image so a container escape does not start with root privileges.
Task
Configure images to run as a non-root numeric user and verify at runtime.
When to use this
Containers run as root by default, so a compromised process has elevated privileges inside the namespace.
Prerequisites
- A Dockerfile you control
- A container runtime
- A test workload
Steps
1. Create a dedicated user and switch to it.
FROM node:22-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
USER 10001:10001
CMD ["node", "server.js"]Expected output: The container starts as uid 10001 rather than root.
2. Verify the effective user at runtime.
docker run --rm app image id -u
kubectl exec deploy/app -- id -uExpected output: Both report uid 10001, confirming non-root execution.
Troubleshooting
Problem
Files not writable by the app user
Solution
Chown writable paths to the app user and avoid world-writable directories.
Problem
Port below 1024
Solution
Bind a high port and forward externally rather than granting capabilities.
Limitations
- Rootless images may still need capability tuning.
- Does not remove capabilities by itself.
- Some third-party images cannot run non-root.
What this recipe does
Configure images to run as a non-root numeric user and verify at runtime.
Containers run as root by default, so a compromised process has elevated privileges inside the namespace.
Prerequisites
- A Dockerfile you control
- A container runtime
- A test workload
Procedure
- Create a dedicated user and switch to it.
FROM node:22-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
USER 10001:10001
CMD ["node", "server.js"]
Expected result: The container starts as uid 10001 rather than root.
- Verify the effective user at runtime.
docker run --rm app image id -u
kubectl exec deploy/app -- id -u
Expected result: Both report uid 10001, confirming non-root execution.
How to confirm it worked
Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows the running process is uid 10001 and the workload functions without root-only operations.
Troubleshooting
Files not writable by the app user — Chown writable paths to the app user and avoid world-writable directories.
Port below 1024 — Bind a high port and forward externally rather than granting capabilities.
Limitations
- Rootless images may still need capability tuning.
- Does not remove capabilities by itself.
- Some third-party images cannot run non-root.