What Docker Compose Does
Docker Compose lets you define and run multi-container applications with a single YAML file. Instead of remembering long docker run commands with every flag, you declare all services, their images, environment variables, ports, volumes, and network connections in a docker-compose.yml, then start everything with docker compose up.
The Basic Structure
version: "3.9"
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:services — each named block defines a container. image specifies which Docker image to pull. ports maps host:container ports. volumes mounts host directories or named volumes into containers. environment sets environment variables.
Build vs Image
If you have a Dockerfile, use build: . instead of image::
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"You can specify a custom Dockerfile path and build arguments using the build block.
Dependencies Between Services
depends_on controls startup order:
services:
app:
depends_on:
- dbNote: depends_on only waits for the container to start, not for the application inside it to be ready. Use a health check and condition: service_healthy if you need to wait until the database is accepting connections.
Networks
By default, all services in a Compose file share a default network and can reach each other by service name. For isolation, define explicit networks:
services:
frontend:
networks:
- public
backend:
networks:
- public
- private
db:
networks:
- private
networks:
public:
private:Environment Variables and .env Files
Compose automatically reads a .env file in the same directory. Variables defined there are available in the docker-compose.yml via ${VARIABLE_NAME}:
environment:
DATABASE_URL: ${DATABASE_URL}Never commit .env files with real credentials. Use .env.example as a template and document required variables there.
Common Errors
Port already in use: another process is listening on the mapped host port. Change the host port mapping or stop the conflicting process. Volume permission errors: the process inside the container may not have permission to write to a bind-mounted directory. Check the UID/GID. Image not found: typo in the image name, or the image requires authentication. Run docker login if it's a private registry. YAML parsing errors: indentation mistakes are the most common cause. YAML uses spaces, not tabs.
Validating Before Deploying
Running docker compose config prints the fully resolved configuration with all variable substitutions applied — useful for checking that .env values are interpolated correctly. A compose validator can catch structural issues (missing required keys, invalid port mappings, unknown top-level keys) before you attempt a deploy. The DevHexLab Docker Compose Validator parses the YAML, checks service definitions, port mappings, and network references, and surfaces issues with explanations.
Production Considerations
Docker Compose is excellent for local development and small-scale deployments. For production at scale, Kubernetes or Docker Swarm provide better scheduling, health management, and rolling updates. Many teams use Compose for local dev and a different orchestrator for production, keeping the two environments as similar as possible.