Skip to main content

Docker Ep 5: Docker Compose for Local Microservices

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
docker - This article is part of a series.
Part 5: This Article
Running 10 docker run commands manually with complex flags is tedious and error-prone. Docker Compose allows you to define and run your entire multi-container infrastructure declaratively in a single YAML file.

TL;DR (Quick Summary)
#

  • Docker Compose: CLI tool (docker compose) for defining multi-container environments.
  • Service Dependencies: Use depends_on with condition: service_healthy to force startup order (e.g., waiting for Postgres to accept connections before starting Node.js).
  • Environment Files: Use .env files to store variables locally without committing secrets to Git.

1. Multi-Container Stack Architecture
#


graph TD
    subgraph Docker Compose Stack (app-network)
        NginxProxy["Nginx Reverse Proxy :80"] -->|Proxy Pass| WebApp["Node.js API Service :3000"]
        WebApp -->|DB Connection| Postgres["Postgres DB :5432"]
        WebApp -->|Cache Connection| Redis["Redis Cache :6379"]
        Postgres --- Volume["('Named Volume: db-data')"]
    end

2. Step-by-Step Lab: Building a Production Multi-Service Stack
#

Let’s write a complete docker-compose.yaml file featuring a Node API, Postgres database, and Redis cache.

The docker-compose.yaml File
#

Create docker-compose.yaml:

version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=postgres
      - DB_PASS=${DB_PASSWORD}
      - REDIS_HOST=redis
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - app-net
    restart: always

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app_db
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app_user -d app_db"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - app-net

  redis:
    image: redis:7-alpine
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

volumes:
  db-data:

The .env File
#

Create .env:

DB_PASSWORD=SuperSecretPass123!

3. Essential Docker Compose CLI Commands
#

# 1. Start all containers in background mode
docker compose up -d

# 2. View combined logs from all services in real time
docker compose logs -f

# 3. Check health status of all running services
docker compose ps

# 4. Stop and remove all containers, networks, and volumes
docker compose down -v

4. Troubleshooting & Common Errors
#

Error 1: API Crashes with ECONNREFUSED on Database Boot
#

The Cause: depends_on without healthchecks only waits for the database container to start, NOT for PostgreSQL to be ready to accept TCP connections! The Fix: Always add a healthcheck block to the database service and set condition: service_healthy on the dependent app service.


Summary & Next Steps
#

In this episode:

  • We orchestrated multi-container architectures using docker-compose.yaml.
  • We configured strict startup dependencies using healthchecks.
  • We passed secrets safely via .env files.

Congratulations! You have completed the Docker Deep Series!

Next, we move to Module 4: Go (Golang) Deep Series (TotalTypeScript / Matt Pocock Depth)!

docker - This article is part of a series.
Part 5: This Article