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:
- Create the user with a fixed number (
-u 10001). Then file ownership on volumes stays the same across rebuilds. - Give the user only the folder it writes to (
/app/data). The code stays owned by root, so the app cannot change itself. - Put
USER appafter 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
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.