Docker Compose Multiple Files Configuration

Overview

Docker Compose supports using multiple configuration files to manage different environments (development, staging, production) through file layering and override patterns.

📋 Table of Contents

Base Configuration Pattern

File Structure

project/
├── docker-compose.yml          # Base configuration
├── docker-compose.dev.yml      # Development overrides
├── docker-compose.prod.yml     # Production overrides
└── docker-compose.test.yml     # Testing overrides

Base Configuration (docker-compose.yml)

version: '3.8'
services:
    app:
        build: .
        ports:
            - "8000:8000"
        volumes:
            - app-data:/data
        environment:
            - NODE_ENV=production

volumes:
    app-data:

Override File Structure

Development Override (docker-compose.dev.yml)

version: '3.8'
services:
    app:
        volumes:
            - ./local-data:/data
            - ./src:/app/src  # Live code reloading
        environment:
            - NODE_ENV=development
            - DEBUG=true
        ports:
            - "3000:8000"  # Different port for dev

Production Override (docker-compose.prod.yml)

version: '3.8'
services:
    app:
        restart: unless-stopped
        environment:
            - NODE_ENV=production
        deploy:
            resources:
                limits:
                    memory: 512M

Command Syntax

Multi-file Deployment

# Development environment
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# Production environment
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Testing environment
docker-compose -f docker-compose.yml -f docker-compose.test.yml up -d

Using Environment Variables

# Set COMPOSE_FILE environment variable
export COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml
docker-compose up -d

Practical Examples

Example 1: Database Configuration Override

Base configuration:

services:
    db:
        image: postgres:15
        environment:
            POSTGRES_DB: myapp

Development override:

services:
    db:
        ports:
            - "5432:5432"  # Expose port for local access
        environment:
            POSTGRES_PASSWORD: devpassword

Example 2: Volume Override Pattern

Result of override: The ./local-data:/data volume from docker-compose.dev.yml completely replaces the app-data:/data volume from the base configuration.

Best Practices

File Organization

  • Keep common configurations in the base docker-compose.yml
  • Use descriptive suffixes (.dev.yml, .prod.yml, .test.yml)
  • Document override purposes in comments

Override Strategy

# ✅ Good: Specific environment tweaks
services:
    app:
        environment:
            - DEBUG=true
        
# ❌ Avoid: Complete service redefinition
services:
    app:
        image: completely-different-image
        # This makes configurations hard to track

Makefile Integration

.PHONY: dev prod test

dev:
    docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

prod:
    docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

test:
    docker-compose -f docker-compose.yml -f docker-compose.test.yml up -d

Common Gotchas

Override Behavior

  • Arrays are replaced, not merged (ports, volumes, environment)
  • Objects are merged (labels, deploy configuration)
  • Later files take precedence when conflicts occur

Environment Variable Priority

# Order matters - last file wins
docker-compose -f base.yml -f override1.yml -f override2.yml up

Volume Mounting Issues

# Base file
volumes:
    - app-data:/data

# Override file  
volumes:
    - ./local-data:/data
    # ⚠️ This completely replaces the base volume, doesn't add to it

Summary

Multi-file Docker Compose configuration enables environment-specific deployments while maintaining a clean base configuration. Use the -f flag to layer configurations, with later files overriding earlier ones for flexible environment management.