This document defines a secure deployment and operations standard for Docker CE, aligned with the CIS Docker Benchmark and designed for use as both:
It establishes a secure-by-default baseline covering the full Docker lifecycle:
The threat model addressed by this standard includes:
This guide is intended to be:
All production Docker CE deployments should comply with this standard unless a documented, risk-accepted exception is approved by Security.
This document provides a full CIS Docker Benchmark–aligned secure deployment guide. Every control from the CIS baseline is mapped into a standard operational format to support:
Each control includes: - What the control requires - Why it matters from a security perspective - How to implement it - How to verify compliance
This section defines the minimum set of controls that must be implemented for any production Docker CE deployment. These represent the highest-risk items from the CIS benchmark and must be in place before a host is approved for production use.
Production Readiness Rule:
A Docker host is not considered production-ready unless all baseline controls above are implemented and verified.
This section covers host-level hardening controls that must be implemented before and alongside Docker CE installation. These controls are foundational: weaknesses here directly undermine all higher-layer protections.
What: Configure Docker’s data directory (/var/lib/docker) on a dedicated filesystem or logical volume.
Why: If container storage shares the root filesystem, runaway images, logs, or layers can exhaust disk space and render the host unavailable. A separate partition also simplifies backup, recovery, and forensic isolation.
How: 1. Create a dedicated LVM or partition. 2. Mount it at /var/lib/docker before installing Docker.
Example:
lvcreate -L 100G -n lv_docker vg0mkfs.ext4 /dev/vg0/lv_dockermount /dev/vg0/lv_docker /var/lib/docker
Persist in /etc/fstab.
Verify (CIS):
df -h /var/lib/docker
Confirm it is not mounted on /.
What: Apply an operating system hardening baseline to the Docker host.
Why: Docker does not protect against host-level compromise. Kernel exploits, weak SSH config, or insecure services immediately expose all containers.
How: - Apply CIS Linux Benchmark (Level 1 or 2) - Disable unused services - Enforce firewall (iptables/nftables) - Harden SSH (PermitRootLogin no, strong ciphers)
Verify (CIS): - CIS OS benchmark scan results - systemctl list-unit-files --state=enabled
What: Install and maintain the latest supported Docker CE release.
Why: Docker regularly patches container escape, privilege escalation, and API vulnerabilities.
How:
apt-get update && apt-get install docker-ce docker-ce-cli containerd.io
Implement regular patching via automation.
docker version --format '{{.Server.Version}}'
What: Restrict membership of the docker group.
Why: Members of the docker group can start privileged containers and effectively gain root access.
getent group dockerusermod -aG docker <approved_user>
Document approved administrators.
getent group docker
What: Audit execution of the Docker daemon binary (dockerd).
Why: Detect unauthorized daemon execution or replacement.
How: Add to /etc/audit/rules.d/docker.rules:
-w /usr/bin/dockerd -p wa -k docker-daemon
Reload audit rules.
auditctl -l | grep dockerd
What: Audit Docker configuration and data directories.
Why: Detect unauthorized modification of images, layers, and configs.
-w /etc/docker -p wa -k docker-config-w /var/lib/docker -p wa -k docker-data
auditctl -l | grep docker
What: Audit access to /var/run/docker.sock.
Why: Access to the Docker socket provides root-equivalent control.
-w /var/run/docker.sock -p wa -k docker-socket
auditctl -l | grep docker.sock
What: Audit Docker systemd service unit files.
Why: Detect persistence via service modification.
-w /lib/systemd/system/docker.service -p wa -k docker-service-w /etc/systemd/system/docker.service.d -p wa -k docker-service
auditctl -l | grep docker-service
What: Audit the Docker client binary.
Why: Detect tampering with the client used to control the daemon.
-w /usr/bin/docker -p wa -k docker-client
auditctl -l | grep docker-client
What: Audit containerd binaries and configuration.
Why: containerd is a core runtime dependency; compromise affects all containers.
-w /usr/bin/containerd -p wa -k containerd-w /etc/containerd -p wa -k containerd
auditctl -l | grep containerd
What: Audit the runc binary.
Why: runc vulnerabilities are a primary container escape vector.
-w /usr/bin/runc -p wa -k runc
auditctl -l | grep runc
What: Audit Docker configuration files such as daemon.json.
Why: Detect insecure configuration changes.
-w /etc/docker/daemon.json -p wa -k docker-config-file
auditctl -l | grep docker-config-file
What: Audit remaining Docker-related paths not covered above.
Why: Ensure full coverage of the Docker execution chain.
How: Audit any additional runtime, plugin, or overlay paths in use.
This section covers hardening of the Docker daemon, which is the most critical control plane component. Misconfiguration here can lead directly to remote root compromise of the host.
All changes in this section should be implemented via /etc/docker/daemon.json and validated with docker info.
What: Disable inter-container communication (ICC) on the default bridge network.
Why: By default, containers on the same bridge can communicate freely, enabling lateral movement if one container is compromised.
How: Edit /etc/docker/daemon.json:
{ "icc": false}
Restart Docker:
systemctl restart docker
docker info | grep -i "Inter Container Communication"
Expected: false
What: Configure the Docker daemon to use INFO log level.
Why: DEBUG logging may expose sensitive information and significantly increase log volume.
{ "log-level": "info"}
ps aux | grep dockerd
Confirm --log-level=info or equivalent in daemon.json.
What: Do not expose the Docker remote API over an unauthenticated TCP socket.
Why: An exposed Docker API grants full root-level control of the host to anyone who can reach the port.
How: - Prefer Unix socket only (default) - If TCP is required, enforce mutual TLS authentication
Example secure config:
{ "hosts": ["unix:///var/run/docker.sock", "tcp://127.0.0.1:2376"], "tlsverify": true, "tlscacert": "/etc/docker/ca.pem", "tlscert": "/etc/docker/server-cert.pem", "tlskey": "/etc/docker/server-key.pem"}
ss -lntp | grep dockerd
Ensure no listener on 0.0.0.0:2375.
What: Enable user namespace remapping so container root is mapped to an unprivileged host UID.
Why: This significantly reduces the impact of container escape vulnerabilities.
{ "userns-remap": "default"}
Create subordinate UID/GID ranges in /etc/subuid and /etc/subgid if not present.
docker info | grep -i "Userns"
Expected: enabled
What: Ensure the default seccomp profile is enforced.
Why: Seccomp filters dangerous system calls and is a primary kernel attack surface reduction mechanism.
How: Default behavior is enabled. Do not disable seccomp with --security-opt seccomp=unconfined.
Optional explicit config:
{ "seccomp-profile": "/etc/docker/seccomp.json"}
docker info | grep -i Seccomp
What: Run Docker with mandatory access control enabled.
Why: MAC policies provide an additional containment layer even after container compromise.
How: - On Ubuntu/Debian: ensure AppArmor is enabled - On RHEL/CentOS: ensure SELinux is enforcing
aa-statusgetenforce
Verify (CIS): - AppArmor: profiles loaded for Docker - SELinux: Enforcing
What: Enable live-restore so containers remain running if the daemon restarts.
Why: Improves availability and reduces disruption during daemon restarts or upgrades.
{ "live-restore": true}
docker info | grep -i "Live Restore"
Expected: true
What: Enable the no-new-privileges security option by default.
Why: Prevents processes from gaining additional privileges via setuid or file capabilities.
{ "no-new-privileges": true}
docker info | grep -i "No New Privileges"
This section ensures that Docker daemon activity is logged, retained, and centrally collected. Logging at this layer is critical for:
All daemon logging should integrate with the organization’s central logging / SIEM platform.
What: Forward Docker daemon logs to a centralized log management or SIEM system.
Why: Local-only logs can be deleted by attackers. Central logging ensures tamper-resistant retention and enables correlation with host and network events.
How: Docker daemon logs are typically written to journald or syslog. Configure your host to forward these logs.
Example (rsyslog forwarding):
# /etc/rsyslog.d/50-docker.confif ($programname == 'dockerd') then @@siem.example.org:514& stop
Restart rsyslog:
systemctl restart rsyslog
If using journald forwarding:
journalctl -u docker.service
Configure systemd-journald remote forwarding as required.
Verify (CIS): - Confirm Docker logs appear in SIEM - journalctl -u docker.service
What: Configure a non-default logging driver for containers that supports centralized logging.
Why: The default json-file driver stores logs locally and is vulnerable to disk exhaustion and tampering.
How: Set a centralized logging driver in /etc/docker/daemon.json.
Example using syslog:
{ "log-driver": "syslog", "log-opts": { "syslog-address": "udp://siem.example.org:514", "tag": "{{.Name}}/{{.ID}}" }}
Alternative drivers include: - journald - fluentd - gelf
Restart Docker after changes:
docker info | grep -i "Logging Driver"
Confirm it is not json-file (unless explicitly approved).
What: Define a standard logging policy for all Docker hosts and containers.
Why: Inconsistent logging leads to blind spots and weak forensic capability.
Recommendations: - Enforce one approved logging driver organization-wide - Standardize log tags (host, container name, image, environment) - Apply log retention and rotation at SIEM level - Monitor for: - Container start/stop - Exec events - Image pulls - Daemon restarts
Verify: - SIEM dashboards show Docker activity - Alerts exist for abnormal patterns
This section addresses image supply chain security and secure build practices. Weak controls here allow vulnerable or malicious images to enter production and undermine all runtime protections.
These controls should be enforced primarily through: - CI/CD pipelines - Image registries - Build standards
What: Only use base images from trusted, approved sources.
Why: Untrusted base images are a primary vector for malware, backdoors, and vulnerable packages entering the environment.
How: - Maintain an internal allowlist of approved base images - Prefer official images or internally built minimal images - Block direct pulls from public registries in production
Example policy: - Allowed registries: registry.company.local - Allowed bases: alpine, debian-slim, internal hardened images
Verify (CIS): - Review Dockerfiles for approved base images - docker history
What: Scan all images for known vulnerabilities before deployment.
Why: Most container breaches exploit known vulnerable packages present in images.
How: Integrate scanning into CI/CD using tools such as: - Trivy - Grype - Clair - Commercial scanners
Example (Trivy):
trivy image myapp:1.2.3
Define policy gates: - Fail build on CRITICAL/HIGH vulnerabilities - Allow documented exceptions only
Verify (CIS): - CI/CD pipeline scan reports - Image registry vulnerability metadata
What: Do not store secrets, keys, tokens, or credentials in images or layers.
Why: Secrets embedded in images are permanently exposed to anyone who can pull the image.
How: - Never use ENV or ARG for secrets - Use runtime secret injection: - Docker secrets - Vault - Kubernetes secrets (if applicable)
Example (bad practice):
ENV DB_PASSWORD=secret
Example (good practice): - Inject at runtime via environment or file mount
Verify (CIS): - Review Dockerfiles - Scan images for secrets using secret scanners
What: Enable Docker Content Trust (image signing and verification).
Why: Prevents tampered or spoofed images from being pulled and executed.
How: Enable globally:
export DOCKER_CONTENT_TRUST=1
In CI/CD, enforce signed images only.
echo $DOCKER_CONTENT_TRUST
Expected: 1
What: Prefer COPY over ADD for adding files to images.
Why: ADD has implicit behaviors (auto-extraction, remote URLs) that increase build-time attack surface.
How: In Dockerfiles:
COPY app/ /app/
Avoid:
ADD http://example.com/file /app/
Verify (CIS): - Review Dockerfiles for ADD usage
What: Use explicit version tags and immutable image references.
Why: Floating tags like latest introduce non-deterministic builds and unplanned changes.
How: - Use semantic versioning: myapp:1.2.3 - Enforce immutability in registry - Disallow :latest in production
Verify: - Registry immutability settings - Dockerfile and deployment manifests
What: Use minimal base images to reduce attack surface.
Why: Smaller images contain fewer packages and vulnerabilities.
How: - Prefer alpine, distroless, or scratch-based images - Remove build tools in final stage
Example (multi-stage build):
FROM golang:1.22 AS buildWORKDIR /srcRUN go build -o appFROM gcr.io/distroless/baseCOPY --from=build /src/app /app
Verify: - Image size review - Package inventory in images
This section governs how containers are executed at runtime. Even with a hardened host and secure images, weak runtime controls can allow:
These controls must be enforced through: - Docker daemon defaults - Deployment standards - CI/CD policy enforcement
What: Do not run containers with the --privileged flag.
Why: Privileged containers disable most kernel isolation mechanisms and grant full access to host devices, effectively equivalent to root on the host.
How: - Prohibit --privileged in production - Enforce policy in CI/CD and admission controls
Verify at runtime:
docker inspect --format '{{.HostConfig.Privileged}}' <container>
Verify (CIS): - Inspect running containers for Privileged=true
What: Run containers as a non-root user.
Why: If an application is compromised, running as root enables easier container escape and privilege escalation.
How: In Dockerfile:
USER appuser
At runtime:
docker run --user 1000:1000 myapp
docker inspect --format '{{.Config.User}}' <container>
Expected: non-empty, non-root user
What: Do not use host namespaces for PID, network, or IPC.
Why: Sharing host namespaces exposes host processes, network stack, and IPC mechanisms to the container.
How: Avoid:
docker run --pid=host --net=host --ipc=host ...
Use default isolated namespaces instead.
docker inspect <container> | grep -i Host
Ensure no host namespace usage.
What: Ensure containers run with the no-new-privileges security option.
Why: Prevents processes from gaining additional privileges via setuid binaries or file capabilities.
How: At runtime:
docker run --security-opt no-new-privileges myapp
At daemon default (recommended):
docker inspect --format '{{.HostConfig.SecurityOpt}}' <container>
Expected: contains no-new-privileges
What: Do not mount sensitive host paths into containers.
Why: Mounting sensitive directories allows direct host file modification and credential theft.
Prohibited mounts: - / - /boot - /etc - /usr - /var/run/docker.sock
How: Review volume mounts in compose files and runtime configs.
docker inspect <container> | jq '.Mounts'
Ensure no sensitive paths are mounted.
What: Drop all unnecessary Linux capabilities.
Why: Many container escapes rely on excessive Linux capabilities.
How: Run containers with minimal capability set:
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
Define a standard baseline capability profile.
docker inspect --format '{{.HostConfig.CapDrop}}' <container>
What: Run containers with a read-only root filesystem where possible.
Why: Prevents attackers from modifying binaries or dropping persistence inside the container.
docker run --read-only myapp
Mount only required writable volumes.
Verify:
docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' <container>
What: Apply CPU and memory limits to all containers.
Why: Prevents denial-of-service conditions caused by runaway containers.
docker run --memory 512m --cpus 1 myapp
docker inspect --format '{{.HostConfig.Memory}}' <container>
This section ensures that Docker deployments are operationally secure over time, not just securely configured at install. These controls address:
This section is critical for SOC integration and incident response.
What: Continuously monitor Docker lifecycle events.
Why: Docker events provide visibility into: - Container creation and deletion - Container start/stop - Exec sessions - Image pulls - Network and volume changes
These are high-signal events for detecting abuse and persistence.
How: Stream Docker events to logging/SIEM:
docker events --format '{{.Time}} {{.Type}} {{.Action}} {{.Actor.Attributes.name}}'
Integrate via: - systemd service - Fluentd / Filebeat input - Custom forwarder
Verify (CIS): - Events visible in SIEM - Test by starting/stopping a container
What: Regularly back up Docker configuration and metadata.
Why: Configuration loss or corruption can prevent recovery after compromise or system failure.
What to back up: - /etc/docker/daemon.json - /etc/docker/ - /var/lib/docker (metadata, not layers, where appropriate)
How: - Include Docker paths in host backup policy - Snapshot volumes containing Docker data
Verify (CIS): - Backup job includes Docker paths - Restore test documented
What: Monitor containers for abnormal or malicious runtime behavior.
Why: Configuration controls cannot prevent all attacks. Runtime detection identifies: - Unexpected process execution - Privilege escalation attempts - Network scanning - File tampering
How: Deploy runtime security tooling, for example: - Falco - Sysdig Secure - eBPF-based sensors - Host EDR with container visibility
Example (Falco rule use cases): - Shell spawned in container - Write to sensitive paths - Privilege escalation syscall
Verify (CIS): - Runtime alerts visible in SOC - Test detection with benign triggers
What: Perform regular reviews of Docker configuration and runtime state.
Why: Drift, ad-hoc changes, and emergency fixes often weaken security posture over time.
How: - Quarterly configuration review - Automated compliance scans against CIS - Review: - daemon.json - Running containers - Image sources - Privileged flags
Verify: - Review records exist - CIS scan reports retained
What: Extend incident response playbooks to explicitly cover Docker environments.
Why: Container incidents require different evidence collection and containment steps than traditional hosts.
How: Document procedures for: - Capturing container state - Preserving images and layers - Exporting container filesystem - Collecting: - docker ps - docker inspect - docker logs - docker events
docker export <container> > container_fs.tar
Verify: - IR playbooks include Docker - Tabletop exercise performed
This table is used for audit evidence, compliance tracking, and operational readiness reviews. It can be used directly as an internal checklist or client assessment worksheet.
CIS ID
Control
Section
Priority
Implemented
Verified
1.1
Separate partition for containers
Host
High
☐
1.2
Host hardened
1.3
Docker up to date
Medium
1.4
Restrict docker group
1.5
Audit dockerd
1.6
Audit Docker directories
1.7
Audit docker.sock
2.1
Restrict ICC
Daemon
2.2
Log level INFO
2.3
No TCP API exposure
Critical
2.4
userns-remap enabled
2.5
Seccomp enabled
2.6
SELinux/AppArmor enforcing
3.1
Central daemon logging
Logging
3.2
Central logging driver
4.1
Trusted base images
Image
4.2
Image vulnerability scanning
4.3
No secrets in images
4.4
Docker Content Trust
5.1
No privileged containers
Runtime
5.2
Non-root containers
5.3
No host namespaces
5.4
No-new-privileges
5.5
No sensitive mounts
5.6
Drop capabilities
6.1
Monitor Docker events
Operations
6.2
Backup Docker configuration
6.3
Runtime threat monitoring
{ "icc": false, "userns-remap": "default", "log-level": "info", "live-restore": true, "no-new-privileges": true, "selinux-enabled": true}
End of CIS-aligned Docker CE secure deployment guide
Was this article helpfu?
Thank you for voting
You are related to multiple companies. Please select the company you wish to login as.