# 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. - Published: 2026-08-03 - Updated: 2026-08-03 - Author: SharpSkill - Tags: docker, docker-compose, devops, containers, networking - Reading time: 12 min --- 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](https://docs.docker.com/compose/compose-file/) 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](/blog/devops/kubernetes-deploying-first-application) 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](/blog/devops/essential-devops-interview-questions). ```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) ## 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](/blog/devops/kubernetes-helm-charts-2026-packaging-deployment-interview-questions) 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/devops/docker-compose-2026-multi-container-networking-devops-interview