Services, Networking, Volumes & Ports
A Basic docker-compose.yml
services:
web:
build: .
ports:
- "8080:80"
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
volumes:
db_data:Dockerfile builds one image; docker-compose.yml runs one or more services together. image uses a pre-built image (postgres:16); build compiles from a local Dockerfile — a typical app mixes both. Services reach each other by service name (web connects to "db") thanks to automatic Compose networking — no manual IP wiring.
Ports vs. Internal Networking
ports: "8080:80" maps HOST:CONTAINER — needed to reach a service from outside Docker (a browser on the host). Internal service-to-service calls (web -> db) don't need a ports mapping at all, just the shared Compose network.
Volumes: Named vs. Bind Mount
A named volume (db_data above) is Docker-managed, ideal for persistent data like a database's files, surviving container recreation. A bind mount maps an explicit host path into the container — common for live-syncing local source code during development.
.env Substitution
${DB_PASSWORD} pulls from a .env file (often git-ignored) — keeps secrets out of the committed compose file while the file itself stays safely checked into version control.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free