try.directtry.direct

Docker Compose: Deploy Multi-Container Applications - Try.Direct Blog

← Back to Articles

Docker Compose: Deploy Multi-Container Applications

What Is Docker Compose?

Docker Compose is a tool for defining and running multi-container Docker applications. It uses YAML files (docker-compose.yml) to configure your application's services, networks, and volumes in a single, declarative format. Instead of running multiple docker run commands, you define everything in one file and launch your entire stack with a single command.

Quick Answer

Docker Compose orchestrates multiple containers using a yaml file. Create docker-compose.yml with services (api, db, redis, etc.), then run docker compose up -d. Compose handles networking, volumes, environment variables, and service startup order automatically.

Installing Docker Compose

Ubuntu 24.04 & Debian 12

Docker Compose comes built-in with modern Docker installations. Verify your installation:

docker compose version

If not installed:

  1. Add Docker repository
  2. Update package lists
  3. Install docker-compose-plugin
  4. Verify installation

Real-World Production Example

Here's a production stack with nginx, Node.js API, PostgreSQL, and Redis:

version: '3.8'

services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - api
    networks:
      - backend

  api:
    build: .
    expose:
      - 3000
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@postgres:5432/app
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - redis
    networks:
      - backend

  postgres:
    image: postgres:16
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=secure_password
      - POSTGRES_DB=app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend

  redis:
    image: redis:7-alpine
    networks:
      - backend

volumes:
  postgres_data:

networks:
  backend:
    driver: bridge

Architecture Overview:

  • Reverse proxy: nginx on ports 80/443
  • Application: Node.js API on port 3000 (internal)
  • Database: PostgreSQL with persistent volume
  • Cache: Redis for sessions and caching
  • Network: All services on internal backend network for security

Common Operations

Start all services

docker compose up -d

View logs

docker compose logs api -f

Scale a service

docker compose up -d --scale worker=4

Restart a specific service

docker compose restart api

Execute command in container

docker compose exec api npm run migrate

Stop and remove everything (with volumes)

docker compose down -v

Environment Variables

Create a .env file in the same directory as docker-compose.yml:

DATABASE_URL=postgres://user:password@postgres:5432/myapp
REDIS_URL=redis://redis:6379
API_PORT=3000
NODE_ENV=production

Reference variables in docker-compose.yml:

services:
  api:
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}

Health Checks

Ensure services start in the correct order:

services:
  postgres:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 10s
      timeout: 5s
      retries: 5
    
  api:
    build: .
    depends_on:
      postgres:
        condition: service_healthy

Networking Between Services

Services can communicate using service names as hostnames:

# From api service, connect to postgres:
const connection = await postgres.connect('postgres://user:pass@postgres:5432/app');

# From api service, connect to redis:
const redis = require('redis').createClient({host: 'redis', port: 6379});

Production Best Practices

  • Use specific image versions - Never use :latest
  • Set resource limits - Prevent runaway containers
  • Use health checks - Ensure proper service startup order
  • Separate concerns - One service per container
  • Secure credentials - Use .env files, never commit secrets
  • Volume backups - Regularly backup persistent data
  • Log aggregation - Use logging drivers for centralized logs

Troubleshooting

Port already in use

docker compose down
# or change port mapping in docker-compose.yml

Service won't start

docker compose logs service_name

Can't reach service from another container

docker compose exec api ping postgres