The most detailed free FDE + DevOps library: 140+ lessons, 70+ labs and 80 long-form articles, in English and Turkish. Start learning →

DevOps Foundations · Module 5: Container basics

Non-root Execution and Container Runtime

Root inside a container is root on the host kernel with one misconfiguration of distance. This lesson makes non-root the default and explains the runtime boundary honestly.

10 min reading

Objectives

  • Run containers as non-root and explain what breaks when you do not
  • Set file ownership inside the image so the runtime user can work
  • Describe what the runtime isolates and what it does not
  • Choose rootless or user namespaces where the environment supports them

Why this matters

A container running as root escapes through a mounted Docker socket or a kernel vulnerability, and the attacker holds the host. The same workload as a numeric non-root user with no new privileges contains the identical exploit to one container. The USER line costs nothing and removes the most common escalation path; skipping it is the container equivalent of chmod 777 from M01.

Concepts

Containers share the host kernel; namespaces and cgroups isolate the view, not the kernel itself. PIDs, mounts, networks and users look private per container, but syscalls reach the same kernel, which is why a kernel exploit or an over-permissive mount (--privileged, the Docker socket, host paths writable) breaks the boundary. The runtime is a convenience boundary with real enforcement, not a security boundary on its own. Defense in depth: non-root user, read-only filesystem where possible, dropped capabilities, no new privileges, minimal mounts.

USER in the Dockerfile sets the runtime user; numeric UIDs (USER 10001) survive environments where names do not resolve. Files the app writes need ownership or group access for that UID, set with chown in the build or an entrypoint that fixes ownership before dropping privileges. A non-root image that crashes on first write was tested as root; test as the runtime user, exactly as M01 lesson 1 demands for services.

Rootless runtimes and user namespaces map container root to an unprivileged host UID, so even a container-root process is nobody outside. Podman runs rootless by default; Docker supports rootless mode. Where available, prefer it: the mapping removes the host-root-escalation class entirely instead of mitigating it per container.

Worked example

A service writes uploads and crashes as non-root. The diagnosis:

$ docker run --user 10001:10001 myapp:1.2 Error: EACCES: permission denied, mkdir '/app/uploads' $ docker run myapp:1.2 ls -ld /app/uploads drwxr-xr-x root root /app/uploads

Expected reading: the directory belongs to root with no group or other write, so UID 10001 cannot create files. The fix belongs in the image, not at runtime flags: chown 10001:10001 the writable paths in the Dockerfile (or a named volume initialized with the right ownership), rebuild, and verify by running as the numeric user with a write smoke test. Runtime --user overrides are for emergencies; baked-in ownership is the fix.

The common wrong move

Running --privileged to make a permission error go away. It disables nearly every isolation the runtime provides (devices, mounts, capabilities) for all processes in the container, permanently, for a problem one chown would fix. Privileged mode is for building runtimes, not running applications; any tutorial step containing it for app code deserves suspicion.

Lab and next step

Lab L13 requires the final image to run as non-root with proof. Next, lesson 3 connects containers to the world: volumes, networks and config.

Quick check

An optional 4-question self-check. Answers never leave your device, are not stored, and never count toward any assessment.

Lesson feedback

No published feedback yet.

Log in and complete the lesson to leave feedback.

Exercise

Run one of your images with --read-only plus --user 10001 and record what breaks. Fix the image (ownership, tmpfs or volumes for writable paths) until it passes; show the before/after run commands.

Pass criteria

Breakage reproduced and quoted; fixed image runs read-only as numeric non-root; every writable path accounted for with its mechanism.

Sources

Log in to track progressFree account: stores only your lesson progress and quiz results.