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 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.
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
# 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.txtThis 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.
# 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: trueThe 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.
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.
# 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 /app/dist ./dist
COPY /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"]# 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.
# 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: 3Three dependency conditions exist:
service_started: Default, container has startedservice_healthy: Health check passesservice_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.
# compose.yaml (base configuration)
services:
api:
build: ./api
environment:
- NODE_ENV=production
db:
image: postgres:16# 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.
# 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:
- apiThe 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.
# .env
POSTGRES_PASSWORD=dev_password_only
API_SECRET_KEY=local_dev_key# 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.envFor production, Docker secrets provide better security by mounting sensitive data as files rather than environment variables:
# 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.txtNever 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:
services:
migrate:
image: api:latest
command: npm run migrate
depends_on:
db:
condition: service_healthy
api:
depends_on:
migrate:
condition: service_completed_successfullySecond, 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:
# 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 configThe 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_healthyconditions ensure proper startup ordering - Use
compose.override.yamlfor 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 composeCLI (V2) replaces the deprecateddocker-compose(V1) with better performance and features
Start practicing!
Test your knowledge with our interview simulators and technical tests.
Tags
Share
Related articles

Kubernetes Interview: Pods, Services and Deployments Explained
Master the three core Kubernetes building blocks — Pods, Services, and Deployments — with production-ready YAML manifests, networking internals, and common interview questions.

Docker: From Development to Production
Complete Docker guide for containerizing applications. Dockerfile, Docker Compose, multi-stage builds and production deployment explained with practical examples.

Kubernetes Helm Charts in 2026: Packaging, Deployment and Interview Questions
Learn Helm chart structure, templating, dependencies, and deployment strategies. Includes common interview questions and best practices for production deployments.