Safeguard
Containers

Docker and Container Security Best Practices: A Combined Checklist

A single, practical checklist covering dockers and containers together — image build, runtime config, and CI gates — instead of treating Docker security and container security as separate problems.

Safeguard Research Team
Research
Updated 5 min read

Teams often treat "Docker security" and "container security" as two separate reading lists, when in practice they're the same problem viewed from two ends of the pipeline. Dockers and containers share one attack surface — the image you build and the runtime you deploy it into — and a checklist that only covers one half will miss the other. This piece walks through both ends together as a single docker security checklist: what to fix in the Dockerfile, what to lock down in the runtime, and where to gate the pipeline so a bad image never reaches production.

Where does container risk actually start?

Container risk starts in the base image, because every package in it is something you now have to patch even if your application never calls it. Pulling FROM ubuntu:latest or FROM node:20 without pinning brings in a full package manager, shell utilities, and dozens of libraries you didn't ask for. The architecture of Docker makes this worse by default: each RUN and COPY instruction creates an immutable layer, so a secret written in one layer and "deleted" in a later one is still recoverable from the image tarball. Fixing this early is cheaper than fixing it later:

  • Pin base images by digest, not by mutable tag, so a compromised upstream push can't silently change what you ship.
  • Prefer slim, distroless, or Alpine-based images over full OS bases to cut the package count — and the CVE count — by an order of magnitude.
  • Add a .dockerignore that excludes .env, .git, and credential files, the same way .gitignore protects your source repo.

What should the Dockerfile itself enforce?

The Dockerfile should enforce non-root execution, multi-stage builds, and explicit version pinning, because these are structural controls a scanner can verify and a reviewer can spot in a diff. A container running as root that gets compromised gives an attacker root on the container immediately, and depending on runtime configuration, a path toward the host. Concretely:

  • Add a dedicated USER directive instead of running as root by default — most base images default to root unless told otherwise.
  • Use multi-stage builds so build tools, source code, and build-time secrets stay in an intermediate stage that never ships.
  • Pin package versions in RUN apt-get install or npm install lines instead of floating to "latest," so a build today and a build next month produce the same image.

What runtime settings do teams forget to lock down?

Teams most often forget readOnlyRootFilesystem, dropped Linux capabilities, and resource limits, because none of these show up as a failed build — they only show up as an easier lateral-movement path during an incident. A container that can write to its own root filesystem gives an attacker a place to drop a second-stage payload; a container that retains NET_RAW or SYS_ADMIN capabilities it never needed gives them tools they shouldn't have. Set:

  • readOnlyRootFilesystem: true wherever the app doesn't need to write to disk, with an explicit writable volume mounted only where required.
  • capabilities.drop: ["ALL"] and add back only the specific capability the app needs, rather than leaving the full default set.
  • CPU and memory limits, so a compromised or runaway container can't starve its neighbors on a shared node.

Where does scanning belong in the pipeline?

Scanning belongs at build time, at merge time, and again on a recurring schedule against images already sitting in the registry — because a clean scan today doesn't stay clean. A base image with zero known CVEs on Monday can have a critical disclosure by Wednesday, and an image that already shipped to production doesn't get automatically re-checked unless something re-checks it. A layered approach with container scanning works better than a single gate:

  • Gate CI on new critical and high findings with a known fix, rather than failing on every finding regardless of severity or exploitability.
  • Re-scan registry images on a schedule, not just at push time, so previously "approved" images don't quietly age into risk.
  • Feed scan results into the same software composition analysis view your SCA tool already produces for application dependencies, so container and application risk sit in one place instead of two dashboards nobody cross-references.

FAQ

Is Docker security different from container security?

Not meaningfully. Docker security usually refers to build-time controls (the image), while container security usually refers to runtime controls (the deployed workload) — but both describe the same artifact at different pipeline stages, and a checklist should cover both.

What's the single highest-impact fix on this list?

Switching from a full OS base image to a minimal or distroless one, because it removes attack surface you'd otherwise have to patch, scan, and justify forever — without requiring any application code changes.

Do these practices apply to Kubernetes deployments too?

Yes. The Dockerfile-level fixes apply to any image regardless of orchestrator, and the runtime settings map directly onto Kubernetes securityContext fields for pods and containers.

How often should base images be rebuilt?

At least weekly, even without an application code change, since upstream OS and language-runtime maintainers ship security patches on their own schedule and a stale image misses them.

Is there a short version of this docker security checklist?

Yes: pin base images by digest, run as non-root, use multi-stage builds, drop all Linux capabilities and add back only what's needed, and scan both at build time and on a recurring schedule against the registry. Those five items catch the majority of real-world container findings.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.