Docker Compose Inspector
Analyze docker-compose.yml files for security issues, exposed ports, privileged containers, and misconfigurations
Enter your docker-compose.yml content to inspect for security issues and misconfigurations
Docker Compose Structure Inspector
Docker Compose files define multi-container application architectures as declarative YAML documents.
A single docker-compose.yml (or compose.yaml following the Compose Specification)
describes services, networks, volumes, configs, and secrets — everything needed to run a distributed
application locally or in production. As projects grow, these files become complex dependency graphs
where services depend on other services, share networks, mount overlapping volumes, and expose ports
that must not collide. This inspector analyzes the structural topology of your Compose files, revealing
how services connect, what their startup order looks like, and where architectural issues may hide.
Unlike security-focused scanners that check for privilege escalation or exposed secrets, this tool examines the architectural blueprint itself: service relationships, network segmentation, volume ownership, port allocation conflicts, and dependency chains. It answers structural questions such as "which services start before the database?" or "do any services share a named volume without explicit intent?" — questions that matter when debugging startup failures, planning horizontal scaling, or onboarding new team members to a complex microservices stack.
Service Dependency Graphs and Startup Order
The depends_on key in Docker Compose establishes directed edges in a service
dependency graph. When service A depends on service B, Compose guarantees that B starts before A.
However, "starts before" does not mean "is ready" — a common source of production failures. The
Compose Specification introduced condition-based dependencies to address this:
- service_started — The default condition. Compose starts the dependency container but does not wait for it to be healthy or fully initialized.
-
service_healthy — Compose waits until the dependency passes its
healthcheckbefore starting the dependent service. This requires a validhealthcheckblock on the dependency. - service_completed_successfully — Compose waits until the dependency container exits with code 0. Useful for migration or seed containers that must finish before the application starts.
This inspector builds the complete dependency graph and detects potential issues: circular
dependencies (which Compose rejects), missing healthchecks on services referenced with
service_healthy, orphan services with no dependents or dependencies, and
excessively deep dependency chains that slow startup. It visualizes the topological sort order
that Compose uses internally, helping you understand exactly which containers launch in parallel
and which block others.
Network Topology and Service Communication
Every Compose project creates a default bridge network that all services join automatically. While convenient for small projects, relying solely on the default network means every service can reach every other service by its container name — no isolation boundaries exist. Custom networks let you segment services into logical tiers:
- Bridge networks — The standard single-host network driver. Services on the same bridge can communicate via DNS names. Services on different bridges are isolated unless they share a network.
- Overlay networks — Span multiple Docker hosts in a Swarm cluster. Required for multi-node deployments where services run on different machines.
-
Custom named networks — Defined in the top-level
networkskey and attached to services via thenetworkslist in each service block. A service can join multiple networks simultaneously.
The inspector maps which services share networks, identifies services that are unreachable from
others (disconnected subgraphs), detects services attached to networks that no other service
uses, and highlights services exposed to multiple network segments. It also checks for port
conflicts: two services publishing the same host port will fail at runtime, but Compose only
reports this at docker compose up time. Catching it during inspection saves
debugging time.
Volume Mappings and Bind Mount Analysis
Volumes in Docker Compose come in two forms: named volumes managed by Docker and bind mounts that map host directories into containers. Each has distinct implications for data persistence, portability, and potential conflicts:
-
Named volumes — Declared in the top-level
volumeskey and referenced by services. Docker manages their lifecycle. Multiple services can mount the same named volume, enabling shared data patterns (but also introducing concurrent write risks). -
Bind mounts — Use host paths directly (e.g.,
./src:/app/src). Common in development for live code reloading. The inspector flags relative paths, missing host directories, and mounts that overlap with named volumes. -
tmpfs mounts — In-memory filesystems that do not persist data. Useful for
sensitive temporary files. Declared with
tmpfs:in the service block.
The inspector identifies services sharing the same named volume (potential data races), bind mounts pointing to the same host path from different services, volumes declared but never referenced (dead declarations), and read-only vs read-write mount permissions. Understanding volume topology is critical when migrating from development to production, where bind mounts must be replaced with persistent storage solutions.
Port Exposure and Environment Configuration
Docker Compose distinguishes between expose (internal container-to-container ports)
and ports (host-mapped ports accessible from outside Docker). The short syntax
"8080:80" maps host port 8080 to container port 80. The long syntax provides
additional control over protocol, host IP binding, and port ranges.
Common structural issues the inspector detects in port configuration:
-
Host port collisions — Two services mapping to the same host port
(e.g., both claiming
0.0.0.0:3000). This fails silently until runtime. - Unnecessary host exposure — Services that only need inter-container communication but publish ports to the host, expanding the attack surface.
- Missing expose declarations — Services that other services depend on but that neither expose nor publish the expected port, causing connection refused errors.
For environment variables, the inspector maps which services use environment
inline keys versus env_file references, checks for duplicate variable definitions
across services, and identifies variables referenced via interpolation
(${VARIABLE}) that may not be defined in any .env file.
Code Examples
Multi-service Compose file with dependency graph and network segmentation
services:
postgres:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
environment:
POSTGRES_DB: appdb
POSTGRES_USER: admin
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d appdb"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
networks:
- backend
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
api:
build: ./api
ports:
- "3000:3000"
networks:
- backend
- frontend
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
DATABASE_URL: postgresql://admin:${DB_PASSWORD}@postgres:5432/appdb
REDIS_URL: redis://redis:6379
worker:
build: ./worker
networks:
- backend
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend
depends_on:
- api
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
volumes:
pgdata:
networks:
backend:
driver: bridge
frontend:
driver: bridgeOutput:
Service Dependency Graph:
postgres (no dependencies) → starts first
redis (no dependencies) → starts first
api → waits for postgres (healthy), redis (healthy)
worker → waits for postgres (healthy), redis (healthy)
nginx → waits for api (started)
Startup Order (topological): [postgres, redis] → [api, worker] → [nginx]
Network Segmentation:
backend: postgres, redis, api, worker
frontend: api, nginx
Port Mappings:
api: 3000 → host:3000
nginx: 80 → host:80, 443 → host:443
Volumes:
pgdata (named): mounted by postgres
./nginx/nginx.conf (bind, read-only): mounted by nginxStandards & Specifications
- Compose Specification — The official Compose Specification defining the schema for compose.yaml files, service configuration, networking, and volume declarations
- Docker Compose File Reference — Docker documentation covering all top-level keys, service options, network drivers, and volume configuration in Compose files
- Compose Specification GitHub — The canonical source for the Compose Specification including service dependencies, healthcheck integration, and extension fields
Frequently Asked Questions
What security issues does the Docker Compose Inspector detect?
The inspector detects privileged mode containers, Docker socket mounts, sensitive host path bind mounts (/etc, /proc, /sys, /dev, /root), exposed ports binding to all interfaces, latest tag or untagged images, undefined environment variables without defaults, unnecessary defined networks, cap_add ALL, and missing memory limits. Each finding includes severity and remediation guidance.
How is the security score calculated?
The score starts at 100 and deducts points based on finding severity: Critical issues (like privileged mode or Docker socket mount) deduct 25 points, High issues (like sensitive bind mounts) deduct 15, Medium issues (like exposed ports or :latest tag) deduct 8, and Low issues (like unnecessary networks or missing memory limits) deduct 3. The final score maps to a letter grade: A (90+), B (75+), C (60+), D (40+), F (below 40).
Is my docker-compose.yml sent to any server?
No. All inspection happens entirely in your browser using JavaScript. Your Docker Compose configuration — which may contain internal service names, registry URLs, environment variables, and infrastructure topology — never leaves your device. No data is stored, logged, or transmitted.
Why is privileged mode flagged as critical?
Privileged mode (privileged: true) gives the container full access to the host kernel and all devices. An attacker exploiting a vulnerability in a privileged container can escape to the host system, access other containers, and potentially compromise the entire infrastructure. Use specific capabilities (cap_add) for the minimum required permissions.
Why is mounting the Docker socket dangerous?
Mounting /var/run/docker.sock gives the container full control over the Docker daemon. A compromised container can then create new privileged containers, access any volume, read secrets from other containers, or delete the entire container infrastructure. If Docker API access is needed, use a proxy like docker-socket-proxy with restricted endpoints.
Why are exposed ports a concern?
Ports mapped as host_port:container_port without an IP bind to 0.0.0.0, making them accessible from any network interface including external ones. This can expose internal services to the internet. Use 127.0.0.1:port:port to bind only to localhost, or use Docker networks with 'expose' (container-only) instead of 'ports' (host-bound).
What format should the input be?
Paste your docker-compose.yml or compose.yaml content as-is. The inspector supports Compose file format versions 2.x and 3.x, including services, networks, volumes, and deploy sections. Multi-service compositions are fully analyzed.
What is the difference between Docker Compose Inspector and Dockerfile Analyzer?
The Docker Compose Inspector analyzes multi-container orchestration — exposed ports, privileged mode, volume mounts, network configurations, and service-level security. The Dockerfile Analyzer focuses on single-container build configuration — base image selection, build-time secrets, user permissions, and layer optimization. Use both for comprehensive Docker security auditing.