Skip to Content
Run the Container as a Non-Root User
Advanced
0 likes
Tutorials

Run the Container as a Non-Root User

Add a user to an image, give it only the folder it writes to, and fix the two permission errors you will most likely hit. After this, your containers stop running as root.

Most images run as root unless you tell them otherwise. This page adds a non-root user to an image, checks it, and fixes the two errors you are most likely to hit. It is the short version of the non-root lesson in the Docker Advanced course. Run on Docker Desktop 4.85.0 (Engine 29.6.2) with python:3.14-alpine.

Check first

docker run --rm --entrypoint id python:3.14-alpine
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

uid=0(root) means root. Run the same check on your own images. --entrypoint id replaces whatever the image normally starts, so it works on almost any image that has the id program.

Add a user in the Dockerfile

import os, pathlib
print("running as uid", os.getuid())
pathlib.Path("/app/data/hello.txt").write_text("written by the app\n")
print(pathlib.Path("/app/data/hello.txt").read_text(), end="")
FROM python:3.14-alpine
RUN addgroup -S -g 10001 app && adduser -S -u 10001 -G app -H app
WORKDIR /app
COPY app.py .
RUN mkdir /app/data && chown app:app /app/data
USER app
CMD ["python", "app.py"]

Three things matter here:

  1. Create the user with a fixed number (-u 10001). Then file ownership on volumes stays the same across rebuilds.
  2. Give the user only the folder it writes to (/app/data). The code stays owned by root, so the app cannot change itself.
  3. Put USER app after every step that needs root, such as installing packages.
Set-Location nonroot-demo
docker build -q -t nonroot-app . | Out-Null
docker run --rm nonroot-app
running as uid 10001
written by the app

Error 1: Permission denied on a folder

If you forget the chown line, the app cannot write:

FROM python:3.14-alpine
RUN addgroup -S -g 10001 app && adduser -S -u 10001 -G app -H app
WORKDIR /app
COPY app.py .
RUN mkdir /app/data
USER app
CMD ["python", "app.py"]
docker build -q -f Dockerfile.broken -t nonroot-broken . | Out-Null
docker run --rm nonroot-broken 2>&1 | ForEach-Object { "$_" } | Select-String "running as|PermissionError"

running as uid 10001
PermissionError: [Errno 13] Permission denied: '/app/data/hello.txt'

Do not fix this by removing USER. Give the user that one folder, as in the working Dockerfile.

Error 2: Permission denied on a named volume

A new named volume takes its owner from the folder in the image the first time it is used. If that folder belongs to app, the volume does too:

docker run --rm -v nonroot-data:/app/data nonroot-app
docker run --rm -v nonroot-data:/app/data --entrypoint ls nonroot-app -ln /app/data
running as uid 10001
written by the app
total 4
-rw-r--r--    1 10001    10001           19 Oct 20 17:23 hello.txt

The file belongs to 10001, the app user. If a volume was first created by a root container, it stays owned by root, and the non-root app gets "Permission denied". Fix the owner once with a short root container (docker run --rm -v VOLUME:/data alpine:3.24 chown -R 10001:10001 /data), or start with a new volume.

Images you don't build

For an image you cannot change, set the user when you run it:

docker run --rm --user 10001:10001 python:3.14-alpine python -c "import os; print('uid', os.getuid())"
uid 10001

This works when the program does not need a named user or a home folder. If it complains, build a small image on top that adds the user.

💡 Tip: Add --cap-drop ALL --security-opt no-new-privileges when you run it. A non-root user with no extra capabilities is a much smaller target. Port 80 still works inside the container on current Docker versions.

Related course: Docker Advanced.

About

Add a user to an image, give it only the folder it writes to, and fix the two permission errors you will most likely hit. After this, your containers stop running as root.

Responsible
Mary Q
Last Update
08/10/2026
Reading Time
4 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.