dockercontainerschecklist

Docker Security Best Practices: A Practical Checklist

YoCyber Team · June 1, 2026 · 3 min read
Share:

Most "Docker security checklists" are 40 items long and treat all of them as equally important. They're not. Here's a shorter list, ordered roughly by how often each one is the actual root cause when a container gets popped.

1. Never run as root inside the container

This is the single highest-leverage fix on this list. If your process runs as root inside the container and an attacker gets code execution, they're root — which makes every subsequent step (escaping to the host, reading mounted secrets, modifying binaries) dramatically easier.

FROM node:20-slim
RUN useradd --uid 1001 --shell /bin/false appuser
USER appuser

Check what you're actually running as right now:

docker exec <container> whoami

If that says root, fix it before anything else on this list.

2. Use minimal base images

node:20 pulls in a full Debian userland — a shell, package manager, dozens of binaries an attacker can use to pivot once they have any code execution. node:20-slim or a distroless image cuts that attack surface dramatically. Fewer binaries in the image means fewer tools available to an attacker who lands inside it.

3. Scan images before they ship, not after

A base image that was clean last month can have a newly disclosed CVE today. Wire image scanning (Trivy, Grype, or your registry's built-in scanner) into CI so a build fails on critical/high vulnerabilities before the image is pushed, not caught in a weekly audit after it's already in production.

4. Set resource limits — every time

An unconstrained container can consume all available CPU or memory on a node, either accidentally (a memory leak) or as a deliberate denial-of-service if compromised. Always set --memory and --cpus (or the Kubernetes equivalent, resources.limits). This isn't optional hardening — it's the difference between one bad container and one bad node.

5. Don't mount the Docker socket into containers

-v /var/run/docker.sock:/var/run/docker.sock is everywhere in tutorials because it's convenient for CI runners that need to build images. It is also functionally equivalent to giving that container root on the host — anything with access to the socket can spin up a privileged container and escape. If you need this for CI, isolate it to dedicated, tightly-scoped runners, never a general-purpose container.

6. Read-only root filesystem where you can

docker run --read-only --tmpfs /tmp your-image

If your app doesn't need to write to its own filesystem at runtime, don't let it. This alone blocks a large class of "attacker drops a second-stage payload onto disk" techniques.

7. Drop Linux capabilities you don't need

Containers get a default set of Linux capabilities, most of which a typical web app never uses.

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE your-image

Start from --cap-drop=ALL and add back only what breaks without it. Most services need zero additional capabilities.

8. Never bake secrets into the image

ENV API_KEY=... or a COPY of a .env file gets baked into every image layer permanently — anyone who can pull the image (or a cached layer) has the secret, forever, even after you "remove" it in a later layer. Use build secrets (--secret with BuildKit) or inject secrets at runtime from a real secrets manager.

Putting it together

Individually these are small changes. Together, they close off almost every common container-escape path that doesn't involve a kernel zero-day. If you want the guided, hands-on version of hardening a real containerized pipeline end to end — not just a checklist — that's the foundation our DevSecOps Masterclass builds from, and it connects directly into CI/CD pipeline hardening once your images are clean.

Share:

Discussion