Hardening a running container

Docker · lesson 19 of 31 · 4 min read

Drop root, freeze the filesystem and remove capabilities so one bug is not a host takeover.

Open this lesson in the learning hub

Key points

  • Root in the container is root on the host kernel. Add a USER with a fixed uid and never run production as root.
  • --read-only freezes the image filesystem. Hand the app a writable --tmpfs /tmp and nothing else.
  • --cap-drop ALL removes the capabilities Docker grants by default. A JVM serving port 8080 needs none of them.
  • --security-opt no-new-privileges blocks setuid escalation, so a stray setuid binary cannot hand root back.
  • Never mount /var/run/docker.sock into an application container. It is a root shell on the host with extra steps.
  • Never reach for --privileged to silence a permission error. Find the single capability you actually need.

Example

# Dockerfile
RUN useradd --system --uid 10001 appuser
USER 10001

# run it with the fence up
docker run -d --name api \
  --user 10001:10001 \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --pids-limit 200 --memory=1g --cpus=2 \
  -p 127.0.0.1:8080:8080 myapp:1.0

# audit what a running container was actually given
docker inspect --format "{{.Config.User}} {{.HostConfig.Privileged}} {{.HostConfig.ReadonlyRootfs}}" api

Non-root, read-only, no capabilities. Four flags, most of the blast radius gone.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Docker course, and every lesson in it is listed on the Docker contents page.