Docker Compose for Beginners: Run a Full Stack Locally

Write your first docker-compose.yml for a web app, database and cache. Learn services, volumes, networks, environment variables and health checks.

Last updated · 2026-08-18

What Compose solves

Modern apps rarely run alone: a web server needs a database, maybe Redis and a reverse proxy. Docker Compose describes all of them in one YAML file so the whole stack starts with a single command — identical on every developer's machine.

A minimal stack

The example below runs a Node.js app built from a local Dockerfile, a PostgreSQL database with persistent storage and Redis. Our docker-compose generator produces files like this for common stacks.

services:
  app:
    build: .
    ports: ["3000:3000"]
    env_file: .env
    depends_on:
      db: { condition: service_healthy }
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
  redis:
    image: redis:7-alpine
volumes:
  pgdata:

Key concepts

Services are containers. Inside the default network they reach each other by service name, so the app connects to the database at host 'db', not localhost.

Volumes persist data. Without the pgdata volume, your database would be wiped every time the container is recreated.

Environment variables keep secrets out of the file. Commit a .env.example with placeholder values — our .env.example generator helps — and keep the real .env in .gitignore.

Health checks combined with depends_on conditions make sure the app starts only once the database is ready to accept connections.

Everyday commands

docker compose up -d starts everything in the background; docker compose logs -f app follows logs; docker compose exec db psql -U postgres opens a database shell; docker compose down stops the stack and down -v also removes volumes.

From development to production

Use a multi-stage Dockerfile to keep images small, pin image versions instead of 'latest', run containers as a non-root user and put Nginx in front for TLS and compression. Our Dockerfile and Nginx config generators give you secure starting points.

Try the tools from this article

← All tutorials