Docker Security Checklist: 50 Points Every Admin Should Verify
Docker containers share the host kernel, which means a misconfigured container can compromise the entire server. The CIS Docker Benchmark provides industry-standard recommendations, but its 200+ pages can be overwhelming. This checklist distills the most critical security controls into 50 actionable items organized by category, each with verification commands and remediation steps.
Score yourself: each item is worth 2 points for a total of 100. A score below 60 indicates significant risk exposure. Between 60 and 80 is acceptable for development environments. Production environments should target 80+.
Section 1: Host Configuration (10 points)
1. Use a dedicated Docker host
Run Docker on a dedicated server or VM, not a shared general-purpose machine. This limits the blast radius of a container escape.
# Verify: check for non-Docker services
systemctl list-units --type=service --state=running | grep -v docker
# Fewer services = better isolation
2. Keep the host kernel updated
Container escapes frequently exploit kernel vulnerabilities. Kernel updates are your first line of defense.
# Check kernel version
uname -r
# Compare against latest stable for your distribution
# Automate updates:
# Ubuntu/Debian: unattended-upgrades
# RHEL/CentOS: dnf-automatic
3. Harden the host operating system
Apply CIS benchmarks for your base OS. Disable unnecessary services, configure firewall rules, and set restrictive file permissions.
# Verify SSH hardening
grep -E "^(PermitRootLogin|PasswordAuthentication|X11Forwarding)" /etc/ssh/sshd_config
# Expected:
# PermitRootLogin no
# PasswordAuthentication no
# X11Forwarding no
4. Use a separate partition for Docker data
Prevent container storage from filling the root filesystem:
# Check Docker data location
docker info --format '{{.DockerRootDir}}'
# Default: /var/lib/docker
# Verify it's on a separate mount
df -h /var/lib/docker
# Should show a dedicated partition, not /
5. Audit Docker files and directories
Enable auditd rules for Docker-related files:
# Add to /etc/audit/rules.d/docker.rules
-w /usr/bin/docker -p wa -k docker
-w /var/lib/docker -p wa -k docker
-w /etc/docker -p wa -k docker
-w /usr/lib/systemd/system/docker.service -p wa -k docker
-w /etc/docker/daemon.json -p wa -k docker
-w /etc/default/docker -p wa -k docker
Section 2: Docker Daemon Configuration (10 points)
6. Restrict network traffic between containers
# In /etc/docker/daemon.json
{
"icc": false
}
# Verify:
docker network inspect bridge --format '{{.Options}}'
# com.docker.network.bridge.enable_icc should be false
7. Set the logging level to info
# In /etc/docker/daemon.json
{
"log-level": "info"
}
# Avoid "debug" in production (leaks sensitive data)
8. Enable user namespace remapping
Map container root to a non-root host UID:
# In /etc/docker/daemon.json
{
"userns-remap": "default"
}
# Verify:
docker info --format '{{.SecurityOptions}}'
# Should include "name=userns"
9. Use a non-default storage driver
# overlay2 is recommended for most distributions
docker info --format '{{.Driver}}'
# Expected: overlay2
# Avoid: aufs, devicemapper (deprecated)
10. Configure default ulimits
# In /etc/docker/daemon.json
{
"default-ulimits": {
"nofile": { "Name": "nofile", "Hard": 65536, "Soft": 65536 },
"nproc": { "Name": "nproc", "Hard": 4096, "Soft": 4096 }
}
}
Section 3: Image Security (10 points)
11. Use trusted base images only
Pull from Docker Official Images or verified publisher images. Never use unverified community images in production.
# Good: official images
FROM python:3.12-slim
FROM node:20-alpine
FROM postgres:16-alpine
# Bad: unverified random images
FROM randomuser/mybase:latest
12. Enable Docker Content Trust
# Enable image signature verification
export DOCKER_CONTENT_TRUST=1
# Verify signed images only
docker pull library/alpine:3.19
# Will fail if the image is not signed
13. Scan images for vulnerabilities
# Using Trivy (integrated with usulnet)
trivy image myapp:latest
# Using Docker Scout
docker scout cves myapp:latest
# Fail builds on critical vulnerabilities
trivy image --exit-code 1 --severity CRITICAL myapp:latest
14. Use minimal base images
Smaller images have fewer vulnerabilities:
| Base Image | Size | Packages | CVE Surface |
|---|---|---|---|
| ubuntu:24.04 | 78 MB | ~100 | High |
| debian:bookworm-slim | 52 MB | ~80 | Medium |
| alpine:3.20 | 7 MB | ~15 | Low |
| distroless (gcr.io) | 2-20 MB | ~5 | Minimal |
| scratch | 0 MB | 0 | None |
15. Do not store secrets in images
# BAD: secrets baked into image layers
COPY .env /app/.env
ENV DB_PASSWORD=s3cur3_p@ss
# GOOD: secrets injected at runtime
# Use Docker secrets, environment variables, or vault integration
Section 4: Container Runtime Security (10 points)
16. Run containers as non-root
# In Dockerfile
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Verify running containers
docker inspect --format '{{.Config.User}}' mycontainer
# Should NOT be empty or "root" or "0"
17. Drop all capabilities and add only what's needed
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
# In Compose
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
18. Set read-only root filesystem
docker run --read-only \
--tmpfs /tmp:rw,noexec,nosuid \
--tmpfs /var/run:rw,noexec,nosuid \
myapp
19. Set memory and CPU limits
docker run \
--memory 512m \
--memory-swap 512m \
--cpus 1.0 \
--pids-limit 100 \
myapp
# Verify limits
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.CPUPerc}}"
20. Enable no-new-privileges
docker run --security-opt no-new-privileges myapp
# Verify
docker inspect --format '{{.HostConfig.SecurityOpt}}' mycontainer
# Should contain "no-new-privileges"
Section 5: Network Security (10 points)
21. Do not use the default bridge network
# Create custom networks
docker network create --driver bridge app-network
# Use in Compose
networks:
app-network:
driver: bridge
22. Restrict container-to-container communication
# Disable ICC on the default bridge
# /etc/docker/daemon.json
{ "icc": false }
# Use explicit network connections
docker network connect app-network container-a
docker network connect app-network container-b
23. Do not map privileged ports unnecessarily
# BAD: exposing on all interfaces
docker run -p 80:80 myapp
# BETTER: bind to specific interface
docker run -p 127.0.0.1:80:80 myapp
# BEST: use a reverse proxy
docker run --network app-net myapp
# Expose only through nginx/traefik reverse proxy
24. Use internal networks for backend services
# In Compose
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No outbound internet access
25. Configure Docker's iptables integration
# Verify Docker iptables rules
iptables -L DOCKER -n
iptables -L DOCKER-USER -n
# Add custom rules to DOCKER-USER chain (survives Docker restarts)
iptables -I DOCKER-USER -s 10.0.0.0/8 -j ACCEPT
iptables -I DOCKER-USER -j DROP
Section 6: Secrets and Data Management (10 points)
26. Use Docker secrets instead of environment variables
# Docker Swarm secrets
echo "mypassword" | docker secret create db_pass -
# In Compose (Swarm mode)
services:
db:
secrets:
- db_pass
secrets:
db_pass:
external: true
27. Encrypt data at rest
Use encrypted storage drivers or filesystem-level encryption for Docker volumes containing sensitive data.
28. Use tmpfs for sensitive runtime data
docker run --tmpfs /run/secrets:rw,noexec,nosuid,size=1m myapp
29. Rotate secrets regularly
Implement automated secret rotation. In Swarm, create a new secret version and update services.
30. Protect the Docker socket
# Verify socket permissions
ls -la /var/run/docker.sock
# Expected: srw-rw---- root docker
# NEVER mount the Docker socket into containers unless absolutely necessary
# If required, use read-only mount and a socket proxy:
docker run -v /var/run/docker.sock:/var/run/docker.sock:ro \
tecnativa/docker-socket-proxy
Section 7: Logging and Monitoring (10 points)
31. Configure centralized logging
# In /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"labels": "service,environment"
}
}
32. Enable container event monitoring
# Monitor Docker events
docker events --filter 'type=container' \
--format '{{.Time}} {{.Action}} {{.Actor.Attributes.name}}'
# Forward to SIEM/log aggregator for alerting
33. Monitor container resource usage
# Real-time monitoring
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"
# Use Prometheus + cAdvisor for historical data
# Or use usulnet's built-in monitoring dashboard
34. Set up alerting for security events
Monitor for: privileged container starts, containers running as root, new image pulls, container escape attempts, and unexpected network connections.
35. Implement runtime security monitoring
# Use Falco for runtime threat detection
docker run -d --name falco \
--privileged \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /dev:/host/dev \
-v /proc:/host/proc:ro \
falcosecurity/falco
Section 8: Orchestration Security (10 points)
36. Encrypt Swarm overlay network traffic
docker network create --opt encrypted --driver overlay secure-net
37. Rotate Swarm join tokens regularly
# Rotate worker join token
docker swarm join-token --rotate worker
# Rotate manager join token
docker swarm join-token --rotate manager
38. Enable mutual TLS for Swarm communication
Swarm enables mTLS by default. Verify the certificate rotation period:
docker info --format '{{.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry}}'
# Default: 90 days. Recommended: 30 days or less
39. Limit manager node count
Use 3 or 5 manager nodes for fault tolerance. More managers increase the attack surface and reduce Raft consensus performance.
40. Use placement constraints for sensitive workloads
docker service create \
--constraint 'node.labels.security-zone==restricted' \
--constraint 'node.role==worker' \
sensitive-app
Section 9: Supply Chain Security (10 points)
41. Pin image digests in production
# Instead of tags (mutable)
FROM nginx:1.27
# Use digests (immutable)
FROM nginx@sha256:abc123...
# Get digest
docker inspect --format '{{.RepoDigests}}' nginx:1.27
42. Use multi-stage builds
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /server .
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /server /server
ENTRYPOINT ["/server"]
43. Implement image signing
# Using Cosign
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
# Verify before deployment
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
44. Scan dependencies in CI/CD
# GitHub Actions example
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
45. Maintain a private registry
Run your own registry to control exactly which images are available in your environment:
docker run -d -p 5000:5000 \
--name registry \
-v /data/registry:/var/lib/registry \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
Section 10: Compliance and Governance (10 points)
46. Run the CIS Docker Benchmark
# Docker Bench Security (automated CIS checks)
docker run --rm --net host --pid host \
--userns host --cap-add audit_control \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /etc:/etc:ro \
docker/docker-bench-security
47. Document your container security policy
Maintain a written policy covering: approved base images, required security scanning thresholds, resource limit requirements, network policies, and incident response procedures.
48. Implement change management for production images
Require pull request approvals for Dockerfile changes. Enforce automated security scans in CI before merging.
49. Conduct regular security audits
Schedule quarterly reviews of: running container configurations, image vulnerability scan results, access control lists, network policies, and secret rotation compliance.
50. Maintain an inventory of all running containers
Know exactly what is running in your environment at all times. Tools like usulnet provide automatic container inventory across all nodes with security metadata.
Scoring Summary
| Score Range | Rating | Recommended Action |
|---|---|---|
| 90-100 | Excellent | Maintain current posture, review quarterly |
| 80-89 | Good | Address remaining gaps within 30 days |
| 60-79 | Acceptable (dev only) | Prioritize critical items, create remediation plan |
| 40-59 | Poor | Immediate attention required for production systems |
| 0-39 | Critical | Stop deploying until core security controls are in place |
Remember: Security is not a one-time configuration. Containers, images, and threats evolve continuously. Automate as many checks as possible and integrate security scanning into your CI/CD pipeline to catch issues before they reach production.