Docker Compose: run a small web app with a database
Docker Compose describes several containers, their ports, volumes and environment in one compose.yaml file, then starts them all with docker compose up.
Most apps are more than one process: a web server, a database, maybe a cache. Starting each with a long docker run command gets old fast. Docker Compose lets you describe them in one YAML file and manage them together. Compose v2 is a Docker CLI plugin, so the command is docker compose with a space. The older docker-compose tool with a hyphen is no longer maintained.
The project layout
myapp/
compose.yaml
.env
web/
Dockerfile
requirements.txt
app.py
The web folder holds a small Python app with its own Dockerfile, such as the one shown in the Docker basics article. The app reads its database address from an environment variable.
compose.yaml
services:
web:
build: ./web
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://app:${POSTGRES_PASSWORD}@db:5432/app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
volumes:
pgdata:
A few things worth noticing:
- Service names are hostnames. Compose puts all services on a shared network, so the web app reaches the database at
db:5432, notlocalhost. - Only
webpublishes a port. The database is reachable by other services but not from outside, which is safer. Addports: ["5432:5432"]todbonly if you need to connect from your machine. - The named volume
pgdatakeeps database files when containers are recreated. depends_onwithservice_healthywaits until the healthcheck passes, not just until the container starts. Without it, the app may try to connect before Postgres accepts connections.- The top-level
version:key from old tutorials is obsolete and can be left out.
Keep secrets in .env
Compose automatically reads a file named .env next to compose.yaml and substitutes ${VARIABLES}. Add .env to .gitignore and commit a .env.example with placeholder values instead.
POSTGRES_PASSWORD=change-me-locally
Everyday commands
docker compose up -d # build if needed, start everything in the background
docker compose ps # status of this project's services
docker compose logs -f web # follow one service's logs
docker compose exec db psql -U app app # open a SQL shell in the db
docker compose up -d --build # rebuild after changing the Dockerfile or dependencies
docker compose restart web # restart one service
docker compose down # stop and remove containers and the network
docker compose down -v # ...and delete volumes: this erases the database
Run these from the folder that contains compose.yaml. Compose names the project after the folder, so two checkouts in different folders do not collide.
Live code reloading in development
Rebuilding the image after every edit is slow. During development, mount your source folder into the container so changes appear immediately:
services:
web:
build: ./web
volumes:
- ./web:/app
command: ["python", "app.py"]
Keep this in a separate compose.override.yaml, which Compose merges automatically when present, so your base file stays production-like.
Troubleshooting
Bind for 0.0.0.0:8000 failed: port is already allocated: something else uses port 8000. Stop it or change the left side of the mapping, for example"8001:8000".- The app cannot reach the database: check that it uses the service name
dbas host, and look atdocker compose logs db. - Changes to the Dockerfile do nothing:
upreuses existing images, so add--build. - Validate the file with
docker compose config, which prints the fully merged configuration or an error.
See also
- 0055 Docker basics: images, containers and the commands you need An image is a read-only package of an app and its dependencies, and a container is a running instance of it. Learn… Beginner · Concept · Linux, macOS, Windows Article Docker basics 15 Oct 2026
- 0250 Containerize a Node.js app and run it with PostgreSQL using Docker Compose Package a small Node.js web app in a multi-stage Docker image, then run it next to PostgreSQL with Docker Compose, named volumes,… Intermediate · Tutorial · Linux, macOS, Windows Article Docker basics · Docker Compose · PostgreSQL 15 Oct 2026
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- commitpedia-admin +3,950 chars