← Recipes

阅读中文版本

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.

~25 mincontainersecurityhardening

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 -u

Expected 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

  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 result: The container starts as uid 10001 rather than root.

  1. 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.