The Container Security Mindset
Containers share the host kernel, so a compromised container can be a stepping stone to the host or other workloads if not properly hardened. Docker security basics focus on defense in depth: reducing what an attacker can do even if application code is compromised, by limiting privileges, minimizing what's present in the image, and catching known vulnerabilities before deployment.
Cricket analogy: A shared kernel is like a shared team dressing room at Eden Gardens: if one careless player leaves the door unlocked, a stranger can wander from the away team's bench straight into the home team's equipment room, so every player must lock their own kit.
Running as a Non-Root User
By default, processes inside a container run as root unless told otherwise, which is dangerous because a container escape or misconfiguration could grant root-level access on the host. Always create and switch to a dedicated non-root user in your Dockerfile using the USER instruction.
Cricket analogy: Letting every net bowler use the main pitch groundskeeper's master key by default is dangerous; instead each bowler should be issued a limited-access pass, just as a Dockerfile's USER instruction switches a container from root to a restricted account.
FROM python:3.12-slim
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["python", "app.py"]Minimizing Attack Surface with Slim and Distroless Images
Every package, shell, and utility in an image is a potential vector for exploitation if an attacker gains code execution. Distroless and slim base images remove shells, package managers, and unnecessary binaries, meaning even if an attacker achieves remote code execution, they have far fewer tools available to escalate or pivot.
Cricket analogy: A stripped-down practice net with no extra equipment lying around gives a trespasser nothing to misuse, unlike a fully stocked pavilion; distroless images similarly remove shells and tools so an attacker who breaks in has nothing to pivot with.
Distroless images have no shell (no /bin/sh), which also means 'docker exec -it <container> sh' will not work — this is intentional hardening, not a bug.
Scanning Images for Known Vulnerabilities
Base images and installed packages regularly accumulate publicly disclosed CVEs. Image scanning tools compare installed package versions against vulnerability databases and should be run in CI before an image is pushed to a registry or deployed.
Cricket analogy: Before the season starts, teams run every player through fitness and injury screening to catch known issues; image scanning tools similarly check installed packages against known CVE databases before an image ships to production.
# Scan a local image with Trivy before pushing
trivy image myapp:1.4.0
# Fail the CI build on HIGH or CRITICAL findings
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:1.4.0Never Bake Secrets into Images
Secrets such as API keys, database passwords, or private certificates must never be added via COPY, ARG, or ENV in a Dockerfile, because they become permanently embedded in image layers and are recoverable even if later layers 'delete' them. Instead, inject secrets at runtime via orchestrator secrets, environment variables passed at container start, or Docker BuildKit's secret mount.
Cricket analogy: Writing a player's confidential medical report directly onto the permanently archived team scorebook means it stays visible in the historical record forever, even if crossed out later; secrets baked into image layers are similarly unerasable.
# WRONG: secret is permanently baked into a layer
ENV DB_PASSWORD=hunter2
# BETTER: use BuildKit secret mount, never persisted in a layer
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=db_password \
DB_PASSWORD=$(cat /run/secrets/db_password) ./configure.shARG values are visible in 'docker history' and image metadata by default. Never pass secrets as build ARGs — use BuildKit secret mounts or runtime injection instead.
Read-Only Filesystems and Dropped Capabilities
Running a container with a read-only root filesystem prevents an attacker or misbehaving process from writing malicious files or tampering with the application at runtime. Similarly, Linux capabilities granted to containers by default (like CAP_NET_RAW) are often unnecessary; dropping all capabilities and adding back only what's required follows the principle of least privilege.
Cricket analogy: Sealing the pitch under groundskeeper supervision so no one can dig it up mid-match, and only granting a bowler the exact run-up length needed rather than the whole ground, mirrors a read-only filesystem plus dropped Linux capabilities.
docker run --read-only \
--tmpfs /tmp \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt=no-new-privileges \
myapp:1.4.0- Always set a non-root USER in the Dockerfile instead of relying on root
- Use minimal or distroless base images to shrink the attack surface
- Scan images for CVEs in CI and fail builds on high/critical findings
- Never bake secrets into image layers; use BuildKit secret mounts or runtime injection
- Run containers with --read-only and --cap-drop=ALL, adding back only needed capabilities
- Use --security-opt=no-new-privileges to block privilege escalation
Practice what you learned
1. Why is it risky to leave a container running as the default root user?
2. Which Dockerfile instruction should never be used to pass a database password?
3. What is the security benefit of a distroless base image?
4. What does '--cap-drop=ALL' followed by a targeted '--cap-add' achieve?
5. Why should image vulnerability scanning happen in CI rather than only manually?
Was this page helpful?
You May Also Like
Writing a Dockerfile
A practical guide to Dockerfile syntax and the core instructions used to build a custom image from scratch.
Image Size Optimization
Techniques for shrinking Docker images through multi-stage builds, minimal base images, and layer-aware Dockerfile authoring to speed up builds and deployments.
Kubernetes Security Basics
Learn defensive Kubernetes security fundamentals: RBAC, NetworkPolicies, Pod security context, secrets handling, and image scanning.
Container Registries
How container registries store, version, and distribute Docker images, covering tagging conventions, authentication, and push/pull workflows.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics