Skip to content
SP StackPractices
beginner By Mathias Paulenko

Local Microservices Development with Docker Compose

Orchestrate multi-service local environments with Docker Compose including databases, caches, message brokers, and reverse proxies with hot reload and shared networks

Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.

Local Microservices Development with Docker Compose

Set up a complete local development environment for microservices using Docker Compose. This recipe covers service definitions, shared networks, volume mounts for hot reload, environment configuration, and health checks that mirror production setups on developer machines.

When to Use This

  • Your application consists of multiple services that must run together locally
  • Developers need consistent environments regardless of host OS
  • Databases, caches, and message brokers are required for integration testing

Solution

1. Multi-Service Compose File

# docker-compose.yml
version: '3.8'

services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - ./api:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://postgres:secret@db:5432/app
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    networks:
      - backend

  worker:
    build:
      context: ./worker
      dockerfile: Dockerfile.dev
    volumes:
      - ./worker:/app
      - /app/node_modules
    environment:
      - DATABASE_URL=postgres://postgres:secret@db:5432/app
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache
    networks:
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - backend

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - backend

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.dev.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - api
    networks:
      - backend

volumes:
  postgres_data:
  redis_data:

networks:
  backend:
    driver: bridge

2. Development Dockerfile with Hot Reload

# api/Dockerfile.dev
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

EXPOSE 3000

CMD ["npm", "run", "dev"]

3. Override File for Local Customization

# docker-compose.override.yml
services:
  api:
    environment:
      - DEBUG=api:*
      - LOG_LEVEL=debug

  db:
    ports:
      - "5432:5432"

4. Health-Checked Startup Script

#!/bin/bash
# start-dev.sh

docker-compose up --build -d

echo "Waiting for services..."
until docker-compose exec -T db pg_isready -U postgres >/dev/null 2>&1; do
  sleep 1
done

echo "Running migrations..."
docker-compose exec api npx prisma migrate dev

echo "Seeding data..."
docker-compose exec api npm run seed

echo "Ready! API: http://localhost:3000"

How It Works

  • Services define each container with build context, image, or both
  • Networks enable DNS-based service discovery between containers
  • Volumes persist database data and enable host code mounts for hot reload
  • depends_on with condition: service_healthy waits for readiness, not just container start
  • override files merge with the base compose for local-specific settings

Variation: Compose Profiles for Selective Startup

services:
  monitoring:
    image: prom/prometheus
    profiles:
      - monitoring
    ports:
      - "9090:9090"
docker-compose --profile monitoring up

Production Considerations

  • Use .env files for secrets; never commit credentials to version control
  • Run docker-compose down -v to clean up volumes when switching branches
  • Keep images small with multi-stage builds for production Dockerfiles

Common Mistakes

  • Mounting node_modules from the host into the container, causing architecture mismatches
  • Forgetting condition: service_healthy, leading to connection errors on startup
  • Using latest tags for base images, causing non-reproducible builds

FAQ

Q: How is this different from Kubernetes? A: Docker Compose is for single-host local development. Kubernetes orchestrates multi-node production clusters.

Q: Can I use Docker Compose in CI/CD? A: Yes, for integration tests. Use docker-compose -f docker-compose.yml -f docker-compose.ci.yml up to override settings for CI environments.