Skip to content

Docker Compose

Container orchestration and service configuration.

Files

File Purpose
docker-compose.yaml Production configuration
docker-compose.local.yaml Local development overrides
Dockerfile Application container

Services Overview

Application Services

services:
  api:
    image: base:local
    ports:
      - "8009:8000"
    command:
      - gunicorn
      - delivery.http.app:wsgi
      - --workers=4
      - --bind=0.0.0.0
      - --timeout=120

  celery-worker:
    image: base:local
    command:
      - celery
      - --app=delivery.tasks.app
      - worker
      - --loglevel=${LOGGING_LEVEL:-INFO}
      - --concurrency=4

  celery-beat:
    image: base:local
    command:
      - celery
      - --app=delivery.tasks.app
      - beat
      - --loglevel=${LOGGING_LEVEL:-INFO}

  bot:
    image: base:local
    command: python -m delivery.bot

Infrastructure Services

services:
  postgres:
    image: postgres:18-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql

  pgbouncer:
    image: edoburu/pgbouncer:latest
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
      POOL_MODE: transaction
      MAX_CLIENT_CONN: 200
      DEFAULT_POOL_SIZE: 25

  redis:
    image: redis:latest
    volumes:
      - redis_data:/data

  minio:
    image: minio/minio:latest
    ports:
      - "9000:9000"
      - "9001:9001"
    command: server /data --console-address ":9001"
    volumes:
      - minio_data:/data

Common Configuration

All application services share common configuration:

x-common: &common
  image: base:local
  env_file:
    - .env
  environment:
    DATABASE_URL: "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@pgbouncer:5432/${POSTGRES_DB}"
    AWS_S3_ENDPOINT_URL: "http://minio:9000"
    REDIS_URL: "redis://default:${REDIS_PASSWORD}@redis:6379/0"
  logging:
    driver: "json-file"
    options:
      max-size: "10m"
      max-file: "3"
  networks:
    - main

Building

Build All Services

docker compose build

Build Specific Service

docker compose build api

No Cache Build

docker compose build --no-cache

Starting Services

Start All

docker compose up -d

Start Specific Services

docker compose up -d api postgres redis

View Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f api

# Last 100 lines
docker compose logs --tail=100 api

Service Dependencies

api:
  depends_on:
    - pgbouncer
    - migrations
    - collectstatic
    - celery-worker

migrations:
  depends_on:
    - pgbouncer

celery-worker:
  depends_on:
    - pgbouncer
    - migrations
    - redis

pgbouncer:
  depends_on:
    postgres:
      condition: service_healthy

Health Checks

PostgreSQL

postgres:
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 10s

PgBouncer

pgbouncer:
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432"]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 5s

MinIO

minio:
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
    interval: 2s
    timeout: 10s
    retries: 5

Volumes

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local
  minio_data:
    driver: local

Local Development Overrides

docker-compose.local.yaml adds development features:

services:
  api:
    volumes:
      - ./src:/app/src:ro
    command:
      - python
      - manage.py
      - runserver
      - 0.0.0.0:8000

  celery-worker:
    volumes:
      - ./src:/app/src:ro
    command:
      - watchmedo
      - auto-restart
      - --directory=/app/src
      - --pattern=*.py
      - --recursive
      - --
      - celery
      - --app=delivery.tasks.app
      - worker
      - --loglevel=${LOGGING_LEVEL:-INFO}

  postgres:
    ports:
      - "5432:5432"

  redis:
    ports:
      - "6379:6379"

Using Local Configuration

The .env.example includes COMPOSE_FILE to automatically configure Docker Compose for local development:

# .env.example
COMPOSE_FILE=docker-compose.yaml:docker-compose.local.yaml

After copying .env.example to .env, you can use docker compose without specifying files:

# Start PostgreSQL, Redis, and MinIO
docker compose up -d postgres redis minio

# Create MinIO buckets, run migrations, and collect static files
docker compose up minio-create-buckets migrations collectstatic

Manual Migrations

You can also run migrations manually using make makemigrations and make migrate.

Scaling

Multiple Workers

docker compose up -d --scale celery-worker=4

API Replicas

docker compose up -d --scale api=3

Load Balancer Required

For multiple API replicas, add a load balancer (nginx, traefik).

Commands

Run Migrations

docker compose exec api python manage.py migrate

Create Superuser

docker compose exec api python manage.py createsuperuser

Django Shell

docker compose exec api python manage.py shell

Database Shell

docker compose exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}

Stopping Services

# Stop all
docker compose down

# Stop and remove volumes
docker compose down -v

# Stop specific service
docker compose stop api