Docker Compose in 2026: Multi-Container Apps, Networking and DevOps Interview Questions

Master Docker Compose for multi-container applications with networking, volumes, and production deployment. Includes common DevOps interview questions with detailed answers.

Docker Compose multi-container architecture diagram showing networking between services

Docker Compose remains the go-to tool for defining and running multi-container Docker applications in 2026. Whether orchestrating a local development stack or preparing for a DevOps interview, understanding Compose networking, service dependencies, and production patterns separates junior engineers from senior practitioners.

Key Takeaway

Docker Compose uses a declarative YAML format to define services, networks, and volumes in a single file. Running docker compose up spins up the entire application stack with isolated networking and persistent storage.

Understanding Docker Compose Architecture

Docker Compose operates on a simple principle: define your application's services in a compose.yaml file (the modern naming convention replacing docker-compose.yml), and Compose handles container creation, networking, and lifecycle management.

The Docker Compose specification defines three core concepts:

  • Services: Container configurations including image, build context, environment variables, and resource limits
  • Networks: Isolated communication channels between services
  • Volumes: Persistent data storage that survives container restarts
yaml
# compose.yaml
services:
  api:
    build: ./api
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/app
    depends_on:
      db:
        condition: service_healthy
    networks:
      - backend

  db:
    image: postgres:16
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=app
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - backend

networks:
  backend:
    driver: bridge

volumes:
  postgres_data:

secrets:
  db_password:
    file: ./secrets/db_password.txt

This configuration demonstrates several production-ready patterns: health checks for dependency ordering, Docker secrets for sensitive data, named volumes for persistence, and explicit network definitions.

Docker Compose Networking Deep Dive

By default, Compose creates a single network for your application. All services join this network and can reach each other using the service name as hostname. Understanding this networking model is fundamental for Kubernetes migration later.

yaml
# compose.yaml
services:
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    networks:
      - frontend-net
      - backend-net

  api:
    build: ./api
    networks:
      - backend-net

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

  db:
    image: postgres:16
    networks:
      - backend-net

networks:
  frontend-net:
    driver: bridge
  backend-net:
    driver: bridge
    internal: true

The internal: true flag prevents the backend network from accessing the external internet. The frontend service bridges both networks, acting as the only entry point. This network segmentation mirrors production security practices.

DNS Resolution

Docker's embedded DNS server resolves service names automatically. The api service reaches the database at db:5432 without any manual IP configuration.

Multi-Stage Builds with Compose

Production images should be minimal. Multi-stage builds combined with Compose profiles enable different configurations for development and production.

dockerfile
# api/Dockerfile
# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:22-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

# Stage 3: Development
FROM node:22-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
USER node
CMD ["npm", "run", "dev"]
yaml
# compose.yaml
services:
  api:
    build:
      context: ./api
      target: ${BUILD_TARGET:-production}
    volumes:
      - ${API_VOLUMES:-./api/dist:/app/dist:ro}
    profiles:
      - ${COMPOSE_PROFILES:-prod}

Running BUILD_TARGET=development COMPOSE_PROFILES=dev docker compose up starts the development configuration with hot reload.

Service Dependencies and Health Checks

The depends_on directive controls startup order, but containers starting doesn't mean services are ready. Health checks solve this timing problem—a pattern frequently asked about in DevOps interviews.

yaml
# compose.yaml
services:
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
      migrations:
        condition: service_completed_successfully

  migrations:
    build: ./api
    command: npm run migrate
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  cache:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3

Three dependency conditions exist:

  • service_started: Default, container has started
  • service_healthy: Health check passes
  • service_completed_successfully: Container exits with code 0 (for init containers)

Ready to ace your DevOps interviews?

Practice with our interactive simulators, flashcards, and technical tests.

Docker Compose for Local Development

Development environments benefit from bind mounts, environment overrides, and debugging tools. The compose.override.yaml file automatically merges with compose.yaml.

yaml
# compose.yaml (base configuration)
services:
  api:
    build: ./api
    environment:
      - NODE_ENV=production

  db:
    image: postgres:16
yaml
# compose.override.yaml (development overrides)
services:
  api:
    build:
      target: development
    volumes:
      - ./api:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
    ports:
      - "9229:9229"

  db:
    ports:
      - "5432:5432"

The anonymous volume /app/node_modules prevents the host's node_modules from overwriting the container's installed dependencies.

Production Deployment Patterns

Docker Compose works for single-host production deployments. For multi-host orchestration, Kubernetes with Helm provides better scaling, but Compose remains viable for smaller applications.

yaml
# compose.prod.yaml
services:
  api:
    image: registry.example.com/api:${VERSION:-latest}
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - api

The deploy key configures resource limits, replica counts, and restart policies. Running docker compose -f compose.prod.yaml up -d starts the production stack in detached mode.

Environment Variables and Secrets Management

Sensitive configuration requires proper handling. Docker Compose supports multiple approaches, from .env files to Docker secrets.

bash
# .env
POSTGRES_PASSWORD=dev_password_only
API_SECRET_KEY=local_dev_key
yaml
# compose.yaml
services:
  api:
    environment:
      - API_SECRET_KEY  # Inherits from shell or .env
      - DATABASE_URL=postgres://user:${POSTGRES_PASSWORD}@db:5432/app
    env_file:
      - ./config/api.env

For production, Docker secrets provide better security by mounting sensitive data as files rather than environment variables:

yaml
# compose.prod.yaml
services:
  api:
    secrets:
      - api_key
      - db_password
    environment:
      - API_KEY_FILE=/run/secrets/api_key

secrets:
  api_key:
    external: true
  db_password:
    file: ./secrets/db_password.txt
Security Note

Never commit .env files containing production secrets to version control. Use external secret management like HashiCorp Vault or cloud provider solutions for production deployments.

Common DevOps Interview Questions on Docker Compose

Interviewers test understanding of Compose concepts through practical scenarios. These questions appear frequently in DevOps and platform engineering interviews.

Q: How do containers in a Compose network communicate?

Docker creates a dedicated bridge network for each Compose project. Services communicate using their service names as hostnames. The embedded DNS server resolves these names to container IP addresses. External traffic reaches services only through explicitly published ports.

Q: What happens when you run docker compose up with existing containers?

Compose compares the current configuration against running containers. Unchanged services continue running. Modified services get recreated with new settings. New services start fresh. Removed services get stopped and deleted.

Q: How do you handle database migrations in Compose?

Two patterns exist. First, use an init container with service_completed_successfully dependency:

yaml
services:
  migrate:
    image: api:latest
    command: npm run migrate
    depends_on:
      db:
        condition: service_healthy

  api:
    depends_on:
      migrate:
        condition: service_completed_successfully

Second, include migration logic in the application startup script, checking and applying pending migrations before accepting traffic.

Q: How do volumes differ from bind mounts?

Named volumes are managed by Docker, stored in /var/lib/docker/volumes, and portable between hosts. Bind mounts map host directories directly into containers, useful for development but environment-dependent. Volumes support drivers for remote storage like NFS or cloud block storage.

Q: What's the difference between docker-compose and docker compose?

The hyphenated docker-compose was the standalone Python-based V1 tool, now deprecated. The space-separated docker compose is the Go-based V2 implementation integrated into Docker CLI. V2 is faster, supports the Compose Specification fully, and receives active development.

Debugging Compose Applications

Troubleshooting containerized applications requires specific techniques. These commands reveal what's happening inside your Compose stack:

bash
# View logs from all services
docker compose logs -f

# Logs from specific service with timestamps
docker compose logs -f --timestamps api

# Execute command in running container
docker compose exec api sh

# Run one-off command (starts new container)
docker compose run --rm api npm test

# View resource usage
docker compose top
docker stats

# Inspect network configuration
docker network inspect project_backend

# Validate compose file
docker compose config

The docker compose config command merges all compose files and shows the final configuration—invaluable for debugging variable interpolation issues.

Conclusion

  • Docker Compose defines multi-container applications in a single YAML file with services, networks, and volumes
  • Services communicate via DNS using service names as hostnames within the default bridge network
  • Health checks with service_healthy conditions ensure proper startup ordering
  • Use compose.override.yaml for development-specific settings like bind mounts and debug ports
  • Production deployments benefit from resource limits, restart policies, and Docker secrets for sensitive data
  • The docker compose CLI (V2) replaces the deprecated docker-compose (V1) with better performance and features

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Tags

#docker
#docker-compose
#devops
#containers
#networking

Share

Related articles