A concise, actionable checklist of Docker best practices to help you build secure, efficient, and maintainable containerized applications.
1. Use official or verified base images
Start FROM official images (e.g., node:20-alpine, python:3.12-slim) from Docker Hub to reduce the risk of supply-chain attacks and bloated layers. Pin to a specific digest or version tag rather than 'latest' to ensure reproducible builds.
2. Minimize image layers by chaining RUN commands
Combine related shell commands into a single RUN instruction using && to reduce the number of layers and overall image size. Clean up package manager caches in the same RUN step (e.g., apt-get clean && rm -rf /var/lib/apt/lists/*).
3. Use multi-stage builds to keep images lean
Separate build-time dependencies from runtime artifacts using multi-stage Dockerfiles (FROM builder AS build … FROM runtime). This can reduce final image size by 80–90% by excluding compilers, test tools, and source files.
4. Run containers as a non-root user
Add a dedicated USER directive in your Dockerfile (e.g., RUN addgroup -S app && adduser -S app -G app; USER app) to limit the blast radius of a container escape or compromised process. Avoid running as UID 0 unless absolutely required.
5. Set explicit WORKDIR instead of using root paths
Always define WORKDIR /app (or an appropriate path) so all subsequent COPY, RUN, and CMD instructions operate in a predictable, isolated directory. This prevents accidental writes to system directories.
6. Leverage .dockerignore to exclude unnecessary files
Create a .dockerignore file to prevent sending node_modules, .git, test fixtures, secrets, and build artifacts to the Docker build context. Smaller build contexts mean faster builds and fewer accidental secret leaks.
7. Avoid hardcoding secrets or environment credentials
Never bake API keys, passwords, or tokens into Dockerfile ENV or COPY instructions; use Docker secrets (docker secret), a secrets manager (Vault, AWS Secrets Manager), or runtime environment injection instead. Scan images for secrets using tools like Trivy or Trufflehog.
8. Declare and expose only necessary ports
Use EXPOSE only for the ports your service genuinely listens on, and map them explicitly at runtime with -p. Avoid exposing all ports or binding to 0.0.0.0 unless required, to reduce your attack surface.
9. Tag images with meaningful, versioned tags
Use semantic version tags (e.g., myapp:2.4.1) or Git commit SHAs alongside a latest alias so rollbacks and audits are straightforward. Never rely solely on latest in production deployments.
10. Use COPY instead of ADD unless you need its extra features
Prefer COPY for straightforward file transfers because it is explicit and predictable; reserve ADD only when you need automatic tar extraction or remote URL fetching. Unexpected behavior from ADD can introduce security risks.
11. Define resource limits for every container
Always set --memory, --cpus, and --pids-limit flags (or their Compose/Kubernetes equivalents) to prevent a single runaway container from starving the host or other services. Start with conservative limits and tune based on profiling.
12. Implement a proper HEALTHCHECK instruction
Add a HEALTHCHECK directive so orchestrators (Docker Swarm, Kubernetes) can detect and restart unhealthy containers automatically (e.g., HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:8080/health || exit 1). Without it, orchestrators only detect crashed processes, not hung ones.
13. Scan images for vulnerabilities in CI/CD
Integrate a container image scanner such as Trivy, Grype, or Snyk into your pipeline to catch CVEs before images reach production. Fail the build on critical or high-severity findings and rebase onto patched base images promptly.
14. Use read-only filesystems and drop Linux capabilities
Run containers with --read-only and mount only the required tmpfs volumes for writable paths. Additionally, use --cap-drop=ALL and selectively re-add only the capabilities your application needs (e.g., --cap-add=NET_BIND_SERVICE) to minimize privilege escalation opportunities.