Hardcoded Plaintext Secrets in Environment
Direct Recommendation: Hardcoding secrets into compose files leads to immediate credential leakage whenever files are pushed to Git or shared in forums. Always isolate secrets into an untracked .env file and reference them using ${VARIABLE_NAME} interpolation.
Risk Analysis: Environment variables like POSTGRES_PASSWORD, API_SECRET_KEY, and DATABASE_URL are readily exposed in version control logs, CI/CD pipeline outputs, and docker inspect commands.
Recommended Remediation
# ✗ Vulnerable:
environment:
- POSTGRES_PASSWORD=my_insecure_password
# ✓ Hardened:
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
# Secrets are stored securely in local .env
Dangerous 'privileged: true' Container Mode
Direct Recommendation: 'privileged: true' grants container processes full root access to host devices and kernel capabilities, completely breaking container isolation. An attacker exploiting an app inside a privileged container can trivially take over the host operating system.
Risk Analysis: Privileged containers can mount arbitrary host block devices, modify host kernel sysctls, and read physical memory. It violates the principle of least privilege.
Recommended Remediation
# ✗ Dangerous:
privileged: true
# ✓ Secure:
# Remove 'privileged: true'. Grant granular Linux capabilities if needed:
cap_add:
- NET_ADMIN
- SYS_TIME
Host Docker Socket Bind Mount (/var/run/docker.sock)
Direct Recommendation: Mounting /var/run/docker.sock gives the container direct API control over the host Docker daemon. Anyone with execution rights in the container can spin up sibling containers with host filesystem access to gain root shell.
Risk Analysis: Commonly used in dashboard or monitoring containers (Portainer, Traefik, Watchtower). If left writable, container compromise equals host root compromise.
Recommended Remediation
# ✗ Writable Socket:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# ✓ Hardened (Read-Only):
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Or route via an isolated Tecnativa docker-socket-proxy
Sensitive Database or Cache Port Bound to 0.0.0.0
Direct Recommendation: Exposing databases and caches to all network interfaces (0.0.0.0) leaves them vulnerable to automated internet scrapers and brute-force attacks. Bind database ports strictly to 127.0.0.1 or keep them purely on internal Docker bridge networks.
Risk Analysis: Redis and Elasticsearch default instances without passwords are top targets for automated botnets and ransomware crawlers scanning public IPv4 ranges.
Recommended Remediation
# ✗ Exposed to Public Internet:
ports:
- "6379:6379"
# ✓ Localhost Only:
ports:
- "127.0.0.1:6379:6379"
# Or omit ports entirely if only sibling containers need access
Missing Container Restart Policy
Direct Recommendation: Without an explicit restart policy, Docker containers remain offline after unexpected process exits or host system reboots. Production services and homelab media servers should specify 'restart: unless-stopped'.
Risk Analysis: If your VPS or NAS restarts for OS updates, containers without restart policies will not boot back up automatically, leading to silent service outages.
Recommended Remediation
# ✓ High Availability Policy:
restart: unless-stopped
Unpinned Floating or ':latest' Image Tag
Direct Recommendation: Using the ':latest' tag introduces unpredictable breaking changes whenever upstream maintainers publish new major releases. Pin services to immutable semantic version tags to ensure consistent, stable deployments.
Risk Analysis: A routine docker compose pull command can accidentally pull a major version database upgrade that corrupts existing schema or migrations.
Recommended Remediation
# ✗ Unpredictable:
image: postgres:latest
# ✓ Reproducible & Pinned:
image: postgres:16.2-alpine
Host Port Collisions Across Compose Services
Direct Recommendation: Docker cannot bind multiple containers to the same host port on the same IP interface. When port collisions occur, the second container will crash during startup with 'bind: address already in use'.
Risk Analysis: Causes deployment failure on docker compose up and disrupts active services running on the shared port.
Recommended Remediation
# Service 1:
ports:
- "8080:80"
# Service 2 (Remapped):
ports:
- "8081:80"
Database Missing Healthcheck Definition
Direct Recommendation: Just because a database container is 'running' does not mean it is ready to accept TCP socket connections. Defining a healthcheck allows backend web services to use 'depends_on: { condition: service_healthy }' to prevent boot race conditions.
Risk Analysis: Web applications that start before the database engine completes initial recovery will throw 500 errors and crash-loop during initial stack launch.
Recommended Remediation
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
Missing Timezone (TZ) Variable
Direct Recommendation: Containers default to UTC unless an explicit TZ environment variable is declared. Setting TZ prevents timestamp discrepancies in application logs, database records, and automated scheduled jobs.
Risk Analysis: Log aggregation and incident investigations become error-prone when host system time and container time differ by several hours.
Recommended Remediation
environment:
- TZ=${TZ:-Etc/UTC}
Deprecated Top-Level 'version' Attribute
Direct Recommendation: The top-level 'version: "3.8"' key is obsolete under the modern Compose Specification (Docker Compose v2+). Modern Docker CLI reads compose.yaml natively and ignores the version attribute.
Risk Analysis: Retaining obsolete version strings produces harmless but annoying warnings during docker compose up execution.
Recommended Remediation
# Simply delete 'version: "3.8"' from the root of your compose.yaml file.
Stateful Database Running Without Persistent Volumes
Direct Recommendation: Containers are ephemeral by design. Running databases without named volumes or host bind mounts means all records, schemas, and uploaded files are irreversibly deleted whenever the container is recreated or upgraded.
Risk Analysis: A single docker compose down followed by up completely wipes the database.
Recommended Remediation
services:
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data: