Question 7 · Section 14

What is Docker Compose?

Docker is like managing individual musicians. Docker Compose is like an orchestra conductor: it knows who enters when, how loud to play, and how musicians hear each other.

Language versions: English Russian Ukrainian

Junior Level

Simple Explanation

Docker Compose is a tool for defining and running multi-container applications. You describe the entire infrastructure (services, networks, volumes) in a single YAML file. If Docker manages individual containers, Docker Compose manages a system of interconnected containers.

Analogy

Docker is like managing individual musicians. Docker Compose is like an orchestra conductor: it knows who enters when, how loud to play, and how musicians hear each other.

Example docker-compose.yml

version: "3.8"

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=jdbc:postgresql://db:5432/mydb
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=secret
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 3s
      retries: 5

# Healthcheck — periodic check whether the service inside the container is alive
# (not just running, but accepting connections).

volumes:
  postgres_data:

Main Commands

Command What it does
docker compose up -d Start all services in background
docker compose down Stop and remove containers and networks
docker compose logs -f Logs of all services in real time
docker compose exec app bash Enter a running container
docker compose ps Show running services

What to Remember

  • Docker Compose describes the entire infrastructure in one YAML file
  • One command docker compose up brings up the entire application
  • Services communicate with each other by names via an internal network
  • Volumes persist data between restarts
  • Ideal for local development

Middle Level

Key Features

  • Declarativity — you describe the desired state in docker-compose.yml.
  • Environment isolation — a separate network is created for each project, allowing multiple copies to run without port conflicts.
  • Dependency managementdepends_on with condition: service_healthy waits for the DB to be ready.
  • Data persistence — volumes for DB data persistence.

Compose is better because: one command instead of N docker run commands, automatic networking between services, declarative description instead of imperative commands.

YAML File Structure

version: "3.8"

services:        # Application containers
  app:
    build: .
    ports: ["8080:8080"]
    environment:
      DATABASE_URL: jdbc:postgresql://db:5432/mydb
    depends_on:
      db: { condition: service_healthy }  # waits for DB readiness, not just container start
    networks: [app-network]

  db:
    image: postgres:15
    volumes: [postgres_data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s
    networks: [app-network]

networks:        # Networks for service communication
  app-network:
    driver: bridge

volumes:         # External storage
  postgres_data:

Typical Mistakes

Mistake Consequence How to avoid
depends_on without healthcheck App starts before DB is ready Use condition: service_healthy
Hardcoded passwords in YAML Secret leakage in git Use .env files
No restart policy Container doesn’t restart on crash restart: unless-stopped
Port conflicts on host “Port already in use” Use different ports or no ports for internal services
Forgetting networks All services on default network, no isolation Create a separate network

When NOT to Use Docker Compose

DON’T use Compose for: production with >1 server, zero-downtime deploy requirements, horizontal scaling, self-healing. For these — use Kubernetes.

Docker Compose in Development vs Production

  • In development — the ideal tool. With one command a developer brings up backend, frontend, Postgres, Redis, and Kafka.
  • In production — used only for small projects on a single server. For scalable systems, orchestrators are used (Kubernetes, Docker Swarm, Nomad).

Useful Features

  1. Environment Variables — support for .env files for storing settings.
  2. Profiles (v3.9+) — run only part of services: docker compose --profile monitoring up.
  3. Healthchecks — Compose waits for full DB readiness before starting dependent services.
  4. Override filesdocker-compose.override.yml for local settings on top of the main file.

What to Remember

  • Docker Compose is one of the most popular tools for local development (alternatives: docker run scripts, devcontainer, Tilt, Okteto)
  • Automates configuration of connections between microservices
  • Don’t store passwords in YAML files, use .env
  • For deployment use Helm charts or K8s manifests
  • depends_on without healthcheck doesn’t guarantee readiness order

Senior Level

Docker Compose as an Architectural Tool

Docker Compose is not just “convenient launch” — it is a declarative description of a microservice system topology. It defines: what services exist, how they are connected, what data is persistent, how service discovery works.

Internal Workings

Docker Compose is not a daemon. It:

  1. Parses docker-compose.yml and builds a dependency graph
  2. Converts the compose model into Docker API calls (create network → create volume → create containers → start)
  3. With depends_on and healthcheck — polls health status before starting dependent services
  4. Creates a Docker network with embedded DNS — services resolve each other by name

Trade-offs

Aspect Docker Compose Kubernetes
Complexity Low (YAML + 1 command) High (many objects)
Scaling Manual (docker compose up --scale) Automatic (HPA)
Self-healing No Yes
Rolling updates No Yes
Service discovery DNS within compose network kube-proxy + CoreDNS
Multi-node No (single host) Yes
Learning curve Hours Weeks/months

Edge Cases

  • depends_on doesn’t wait for readiness by default: depends_on guarantees only the start order of containers, not the readiness order. The DB service may have started but not yet accepting connections. Solution: condition: service_healthy + healthcheck.
  • Network name resolution: Compose creates a network with DNS. Service db resolves as db. But if docker compose up is run from different directories, the networks are different — db in project A ≠ db in project B.
  • Volume naming: postgres_data in Compose gets a project prefix: myproject_postgres_data. This can break migrations when renaming the project.
  • Resource limits: Compose v3 supports deploy.resources, but only for Docker Swarm. For standalone Docker use v2 syntax mem_limit, cpus.
  • Windows paths: on Windows volumes are mounted via CIFS/SMB. Issues with permissions, performance (especially with many small files), symlinks.

Production Approach for Small Systems

version: "3.8"

services:
  app:
    image: myregistry.com/app:${APP_VERSION:-latest}  # pin version
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 1G
        reservations:
          cpus: "0.5"
          memory: 512M
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DATABASE_URL=jdbc:postgresql://db:5432/mydb
    depends_on:
      db: { condition: service_healthy }
    networks: [app-net]
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:15.4-alpine
    restart: unless-stopped
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks: [app-net]

volumes:
  postgres_data:
    driver: local

networks:
  app-net:
    driver: bridge

Security

  • Don’t store secrets in YAML — use .env files added to .gitignore.
  • Docker secrets — for Swarm mode: echo "password" | docker secret create db_pass -.
  • Read-only containersread_only: true + tmpfs for temporary files.
  • Non-root useruser: "1000:1000" in service definition.
  • Network isolation — separate networks for frontend/backend services.

Performance

Metric Compose (single host) Kubernetes (cluster)
Max services ~50-100 (host limit) Thousands
Startup time all services 10-60s 1-5 minutes
RAM consumption Minimal (containers only) 500MB+ (system components)
Network overhead Bridge network, minimal kube-proxy, CNI overlay

Migration Compose → Kubernetes

Conversion tools exist:

  • Komposekompose convert -f docker-compose.yml → K8s manifests
  • Docker Compose for K8s (experimental) — docker compose up directly to K8s
  • Okteto — development in K8s with Compose-like DX

But automatic conversion doesn’t give production-ready K8s manifests. You need to manually configure: HPA, PDB, ingress, liveness/readiness probes, resource quotas.

Production Story

A startup of 5 developers used Docker Compose for production on a single VPS (8 CPU, 16GB RAM). Application: 6 microservices + PostgreSQL + Redis + Nginx. It ran stably for 18 months. When traffic grew 10x, Compose was no longer sufficient: no horizontal scaling, no rolling updates, no self-healing. Migration to Kubernetes took 3 weeks but gave: automatic scaling under load, zero-downtime deploy, automatic recovery on crash. Compose remained for local development — as “launch documentation.”

Monitoring

  • docker compose ps — service status
  • docker compose top — processes in containers
  • docker stats — real-time CPU/RAM/network
  • Log aggregation: driver: json-file + fluentd/Fluentbit for centralized logging
  • Healthcheck status: docker compose ps shows unhealthy services

Summary

  • Docker Compose is the standard for local development and prototyping.
  • Allows automating the configuration of connections between microservices.
  • depends_on without healthcheck doesn’t guarantee readiness order — a critical mistake.
  • Acceptable for production on a single server, but with limitations (no scaling, no self-healing).
  • In large systems — Compose as “local launch documentation”, for deployment — K8s.
  • Security: .env for secrets, non-root user, read-only filesystem.

Interview Cheat Sheet

Must know:

  • Docker Compose — declarative launch of multi-container applications from a single YAML file
  • Services communicate via internal network by names (embedded DNS)
  • depends_on without condition: service_healthy guarantees start order, not readiness
  • Volumes ensure data persistence between container restarts
  • Compose is the standard for local development; for production — Kubernetes
  • Override files (docker-compose.override.yml) for local settings
  • Kompose converts Compose → K8s manifests (but not production-ready)

Frequent follow-up questions:

  • “Why is depends_on without healthcheck insufficient?” — DB container started, but not yet accepting connections
  • “How do services find each other?” — Compose creates a network with DNS; service db resolves as db
  • “Can Compose be used in production?” — Yes, for small projects on a single server (no HA, scaling)
  • “What are profiles in Compose?” — Allow running only part of services (--profile monitoring)

Red flags (DO NOT say):

  • “Compose replaces Kubernetes” (no HA, self-healing, rolling updates)
  • “I store passwords directly in docker-compose.yml” (use .env, Docker secrets)
  • “depends_on guarantees DB is ready” (only guarantees start order)
  • “Compose works across multiple servers” (single host only)

Related topics:

  • [[What is Kubernetes and why is it needed]] — orchestration for production
  • [[What is containerization and why is it needed]] — containerization basics
  • [[How to monitor applications in Kubernetes]] — monitoring for production