Docker from First PrinciplesPost 13 of 18 · 18 of 18 written
- How to use this guide
- 01The problem Docker solves
- 02What a container really is
- 03Images: layers, hashes and registries
- 04Docker's architecture
- 05Container lifecycle
- 06Dockerfiles in depth
- 07Storage: where data lives
- 08Networking
- 09Docker Compose for multi-service AI apps
- 10GPUs in containers
- 11Building good images for AI workloads
- 12Serving and production patterns
- 13Security
- 14Debugging and introspection
- 15Beyond Docker
- 16Hands-on learning path
- 17Question bank
- 18Glossary and cheat sheet
13. Security
Containers isolate well enough for most workloads, but more weakly than virtual machines, because every container shares the host kernel. The real-world risks are rarely exotic escapes; they’re running as root, mounting the Docker socket, leaking secrets into layers, publishing ports to the internet, and trusting unverified images and model files.
The main risks and their fixes
- Root in the container is root on the host’s kernel. Without user namespaces, UID 0 inside is UID 0 outside; only namespaces, capabilities and seccomp stand between it and the host. Set
USERin images, and consider rootless Docker, Docker’suserns-remap, or Podman’s rootless mode, which map container root to an unprivileged host user. - Mounting
docker.sockhands over the host. A container with-v /var/run/docker.sock:/var/run/docker.sockcan start privileged containers and read any file on the host. CI runners, dashboards and some agent frameworks ask for this; know that you’re granting root. --privilegeddisables isolation. If something needs one device or one capability, grant exactly that with--deviceor--cap-add.- Least privilege by default. For services,
--cap-drop ALL(then add back only what’s needed),--security-opt no-new-privileges, and--read-onlywith a tmpfs for/tmpremove most of what an attacker could use. - Secrets leak through layers and metadata.
ARGandENVvalues show up indocker historyanddocker inspect.COPY .envfollowed byRUN rm .envleaves the file in the earlier layer. Use BuildKit secret mounts at build time and runtime injection afterwards. - Published ports bypass host firewalls on Linux (Part 8). Bind internal services to
127.0.0.1and put authentication in front of anything public.
Supply chain
Every FROM trusts someone else’s code. Prefer official or verified publishers, pin digests, and rebuild regularly to pick up security patches. Scan images with Docker Scout, Trivy or Grype. Generate a software bill of materials (docker buildx build --sbom=true) and provenance attestations, and sign images with Sigstore’s cosign when others will consume them.
Model files are code too
PyTorch’s classic .pt and .bin checkpoints use Python’s pickle format, which can execute arbitrary code when loaded. Prefer safetensors files, and load anything else with torch.load(..., weights_only=True), which recent PyTorch versions use by default. Treat a random checkpoint from the internet like a random executable.
Sandboxing LLM-generated code
Agents that run model-written code need stronger isolation than a plain container offers. Layer the defences: no network (--network none) or an egress allowlist, a read-only root filesystem, a non-root user, tight CPU, memory and PID limits (--pids-limit), a short timeout, and a stronger runtime. gVisor (--runtime=runsc) intercepts system calls in a user-space kernel; Firecracker and Kata Containers run each container in a lightweight VM. Hosted code-execution sandboxes for agents are built on these same pieces.