← Docker Compose Security Check
privileged, docker.sock, exposed ports and how to fix eachA Compose file decides how much of the host each container can touch. Most compose-related incidents are not clever exploits. They are one line that removed a boundary, such as a database published on every interface or a credential committed in plain text.
# hands the host to the container
services:
web:
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# narrow, loopback-only, pinned
services:
db:
image: postgres:16
ports: ["127.0.0.1:5432:5432"]
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
privileged: true. Fix: delete it; grant one device or capability instead./var/run/docker.sock mounted. Root on the host, even with :ro. Fix: remove it or use a socket proxy.network_mode: host. Fix: publish only the ports you need.cap_add: [ALL] or SYS_ADMIN. Fix: cap_drop: [ALL] and add back one narrow capability."127.0.0.1:5432:5432" or no ports:.environment:. Fix: .env outside version control or Docker secrets, then rotate. The tool never prints the value./, /etc, /root, /boot, /proc or /sys mounted. Fix: mount one directory, read-only when possible.seccomp:unconfined, apparmor:unconfined or label:disable. Fix: delete it so Docker's default profiles apply.pid: host or ipc: host. Fix: use the private default.user: root, 0 or 0:0. Fix: user: "1000:1000". Medium.:latest. Fix: pin a version or digest. Medium.restart: unless-stopped. Low.service_healthy. Low.Rarely. It disables most of the isolation Docker provides, so a compromised container can load kernel modules, read host devices and escape to the host. Almost every real need is narrower: map one device with devices:, or add one capability such as NET_ADMIN with cap_add, and keep privileged off.
No. The Docker API is root on the host: a container that can reach it can start another container with the host filesystem mounted. A :ro suffix does not restrict the API. If a dashboard or CI agent needs it, use a socket proxy that exposes only the read-only endpoints required.
Bind it to loopback in ports: with "127.0.0.1:5432:5432", or remove ports: entirely, since other services on the same Compose network reach it by service name. Remember Docker writes its own firewall rules, so a host firewall such as ufw does not block a port you published on 0.0.0.0.
Paste your compose file into Docker Compose Security Check to see every finding with its line, risk and fix, or run the CLI in your own repository:
python3 check.py --fail-on-risk
That exits with a nonzero status on any medium or high finding, so you can wire it into your own CI. Expect some findings to be deliberate (a monitoring agent may truly need the socket); review each one.