Security
Secure Docker deployments — image scanning, runtime security, secrets management, network isolation, and production hardening.
Image Security
Start with minimal, trusted base images from official sources. Scan every image for CVEs before deployment. Pin images by digest, not tag, for reproducible and verified builds.
Remove unnecessary packages and tools from production images — no shell, no package manager, no compiler. Distroless and scratch images minimize attack surface.
- Sign images with cosign for supply chain verification
- Enable Docker Content Trust (DCT) for signed pulls
- Rebuild images regularly to incorporate base image patches
# Scan before deploy trivy image --severity CRITICAL,HIGH myapp:latest # Pin by digest FROM node:20-alpine@sha256:exact-digest-here
Runtime Security
Run containers as non-root users. Add USER node or create a dedicated user in your Dockerfile. Use --read-only to mount the root filesystem as read-only with tmpfs for writable directories.
Drop capabilities with --cap-drop=ALL and add only what is needed. Never run containers with --privileged unless absolutely required.
FROM node:20-alpine RUN addgroup -S app && adduser -S app -G app USER app WORKDIR /app COPY --chown=app:app . . CMD ["node", "server.js"] # Run read-only docker run --read-only --tmpfs /tmp myapp
Secrets Management
Never embed secrets in images or environment variables in compose files committed to git. Use Docker secrets (Swarm mode), external secret managers (Vault, AWS Secrets Manager), or runtime injection.
Mount secrets as files, not environment variables — environment variables appear in docker inspect output and process listings.
# Docker Swarm secrets echo "db-password" | docker secret create db_password - docker service create --secret db_password myapp # Runtime injection (preferred for Compose) docker run -e DATABASE_URL \ -e DATABASE_URL=$(vault kv get -field=url secret/db) myapp
Network Security
Segment networks by function — frontend, backend, and database tiers on separate networks. Use internal: true to prevent external access to backend networks.
Implement least-privilege networking: only expose ports that need external access. Use a reverse proxy as the single entry point with TLS termination.
networks:
frontend:
backend:
internal: true
database:
internal: true
services:
web:
networks: [frontend, backend]
api:
networks: [backend, database]
db:
networks: [database]Security Scanning in CI
Integrate image scanning into your CI pipeline. Fail builds on critical CVEs. Use Hadolint to lint Dockerfiles for security anti-patterns. Audit running containers with Docker Bench for Security.
Establish a vulnerability response process: scan daily, patch within SLA, and maintain an SBOM (Software Bill of Materials) for compliance.
# Hadolint Dockerfile linter hadolint Dockerfile # Docker Bench Security docker run --rm --net host --pid host \ -v /var/run/docker.sock:/var/run/docker.sock \ docker/docker-bench-security