Docker from Basics to Production · Containerize Real Applications

A Development Stack with Docker Compose

Run an API, PostgreSQL and Redis together with Compose: service names, healthchecks, volumes and live code reloading.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A compose.yaml that starts PostgreSQL and Redis with healthchecks and named volumes, plus an API container that connects to both by service name once they are healthy. One command brings the stack up; one takes it down.

The idea

  • Service names are hostnames on the project network: the API connects to db:5432 and cache:6379.
  • Healthchecks + condition: service_healthy make the API wait for databases that are actually ready.
  • Named volumes keep database data between down and up (unless you pass -v).
  • For development, bind-mount the source into the API container and run with auto-reload; docker compose watch can sync or rebuild on file changes.
  • Publish database ports only to 127.0.0.1 if you publish them at all.

The code

Bring up PostgreSQL, Redis and an API that waits for both
trap 'docker compose -p rc-stack down -v > /dev/null 2>&1' EXIT
cat > compose.yaml <<'EOF'
services:
  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_PASSWORD: devpassword
      POSTGRES_DB: shop
    volumes: [pgdata:/var/lib/postgresql]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d shop"]
      interval: 1s
      retries: 30
  cache:
    image: redis:8-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      retries: 30
  api:
    image: postgres:18-alpine          # stands in for your API image; it has psql for this demo
    environment:
      PGPASSWORD: devpassword
    command:
      - sh
      - -c
      - |
        psql -h db -U postgres -d shop -tAc "create table orders(id int); insert into orders values (1),(2);" > /dev/null
        echo "orders in db: $(psql -h db -U postgres -d shop -tAc 'select count(*) from orders')"
        echo "cache says: $(printf 'PING\r\n' | nc -w 1 cache 6379 | tr -d '\r+')"
    depends_on:
      db: {condition: service_healthy}
      cache: {condition: service_healthy}
volumes:
  pgdata:
EOF
docker compose -p rc-stack up -d db cache --wait > /dev/null 2>&1
docker compose -p rc-stack run --rm api 2>/dev/null
docker compose -p rc-stack ps --format '{{.Service}}: {{.Health}}' | sort
echo "volume kept for next time: $(docker volume ls --filter name=rc-stack_pgdata --format '{{.Name}}')"

Output

orders in db: 2
cache says: PONG
cache: healthy
db: healthy
volume kept for next time: rc-stack_pgdata

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • depends_on without a condition, so the API starts before the database accepts connections.
  • Connecting to localhost from one container to reach another; use the service name.
  • Committing real credentials in compose.yaml; use a git-ignored .env file for anything sensitive.
  • Running docker compose down -v by habit and losing your local data.

Key points

  • Compose gives every service a DNS name on a project network.
  • Healthchecks plus service_healthy conditions order startup by readiness.
  • Named volumes persist data; bind mounts and watch speed up development.

Further reading: Compose file reference · Compose watch

Practice questions

How does a Compose service connect to a database service called db?

Answer

By the hostname db on the project network that Compose creates; never localhost, which would be the service's own container.

What does docker compose up --wait do?

Answer

Starts the services in the background and waits until they are running and healthy (for services with healthchecks) before returning, which is useful in scripts and CI.

Where should local development secrets for Compose live?

Answer

In a git-ignored .env file or env_file referenced by compose.yaml, not committed to the compose file itself.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?