Skip to main content
Settings
Search
Appearance
Theme Mode
About
Jekyll v3.10.0
Environment Production
Last Build
2026-06-18 05:38 UTC
Current Environment Production
Build Time Jun 18, 05:38
Jekyll v3.10.0
Build env (JEKYLL_ENV) production
Quick Links
Page Location
Page Info
Layout quest
Collection quests
Path _quests/0100/docker-compose-orchestration.md
URL /quests/0100/docker-compose-orchestration/
Date 2025-11-29
Theme Skin
SVG Backgrounds
Layer Opacity
0.6
0.04
0.08

Docker Compose Orchestration: Multi-Container Apps

Master Docker Compose to orchestrate multi-container apps. Define services, networks, volumes, depends_on, and environment variables in one YAML file.

Table of Contents

⚔️ Lvl 0100Adventurer 🏰 Main Quest 🟡 Medium 75-90 minutes

Docker Compose Orchestration: Multi-Container Apps

Build and orchestrate multi-container applications with Docker Compose - services, networks, volumes, and environment configuration

Primary Tech
🛠️ docker-compose
Skill Focus
Devops
Series
Docker Mastery
Author
IT-Journey Team
XP Range
⚡ 1000-1500

Greetings, brave adventurer! You have already forged single containers in the fires of the Container Coast. But real applications are rarely a lone process - they are a web server, a database, a cache, and a background worker, all needing to find each other, share secrets, and start in the right order. Coordinating them by hand with a dozen docker run flags is a path to madness.

This quest, Docker Compose Orchestration, hands you the conductor’s baton. With a single declarative file and one command, you will bring an entire ensemble of containers to life, wire them together, and tear them down just as cleanly.

📖 The Legend Behind This Quest

When developers first containerized their apps, they discovered a new problem: an app and its database are two containers, and keeping them in sync through raw docker run invocations was error-prone and impossible to share. Each teammate had a slightly different startup ritual, and the old curse - “but it starts on my machine” - threatened to return.

Docker Compose answered with a simple idea: describe the whole stack in one YAML file, commit it to the repository, and let docker compose up reproduce the exact same environment for everyone. The file becomes the single source of truth - infrastructure you can read, review, and version like code.

Master Compose and you hold the keystone of local development environments and the on-ramp to every orchestration tool that follows, from CI pipelines to Kubernetes.

🎯 Quest Objectives

By the time you complete this journey, you will have mastered:

Primary Objectives (Required for Quest Completion)

  • The Compose File - Write a compose.yaml that defines multiple services declaratively
  • Services & Networking - Let containers discover and talk to each other by service name
  • Volumes - Persist database data so it survives container restarts and rebuilds
  • Lifecycle Commands - Bring the stack up, view logs, and tear it down with single commands

Secondary Objectives (Bonus Achievements)

  • depends_on & Health - Control startup order and wait for dependencies to be healthy
  • Environment & .env - Configure services with environment variables and an .env file
  • Build vs Image - Mix locally built services with pulled images in one stack

Mastery Indicators

You’ll know you’ve truly mastered this quest when you can:

  • Explain how one service reaches another using only its service name
  • Describe why a named volume keeps data that a container’s writable layer would lose
  • Add a new service to an existing stack without breaking the others
  • Move a secret out of the compose file and into an .env file

🗺️ Quest Prerequisites

📋 Knowledge Requirements

  • Completion of Container Fundamentals (images, containers, build, run)
  • Comfort building and running a single container
  • Ability to edit YAML carefully (indentation matters)

🛠️ System Requirements

  • Modern operating system (Windows 10+, macOS 10.14+, or Linux)
  • Docker with the Compose plugin (docker compose version should work)
  • A terminal and a text editor or IDE (VS Code recommended)
  • Internet connection for pulling base images

🧠 Skill Level Indicators

This 🟡 Medium quest expects:

  • You completed Container Fundamentals or can already build and run images
  • You are comfortable with precise YAML indentation
  • Ready for 75-90 minutes of focused, hands-on learning

🌍 Choose Your Adventure Platform

Compose ships as a plugin inside modern Docker, so once Docker itself runs, Compose runs everywhere the same way. Verify it before you begin: docker compose version.

🍎 macOS Kingdom Path

Click to expand macOS instructions ```bash # Docker Desktop already bundles the Compose plugin docker compose version # If you installed only the engine via colima, add the plugin brew install docker-compose ``` **macOS-Specific Notes:** - With Docker Desktop, no separate install is needed. - Use the modern `docker compose` (space) form, not the legacy `docker-compose` (hyphen) binary.

🪟 Windows Empire Path

Click to expand Windows instructions ```powershell # Docker Desktop on Windows includes Compose docker compose version # Run commands from a WSL 2 distro for the smoothest experience wsl docker compose version ``` **Windows-Specific Notes:** - Keep your project files inside the WSL 2 filesystem for fast bind mounts. - The Compose plugin is installed with Docker Desktop automatically.

🐧 Linux Territory Path

Click to expand Linux instructions ```bash # Install the Compose plugin if it isn't already present sudo apt update && sudo apt install -y docker-compose-plugin # Debian/Ubuntu # sudo dnf install -y docker-compose-plugin # Fedora/RHEL docker compose version ``` **Linux-Specific Notes:** - Native Docker on Linux gives the fastest builds and bind mounts. - If `docker compose` is missing, the `docker-compose-plugin` package provides it.

☁️ Cloud Realms Path

Click to expand Cloud/Container instructions ```bash # Codespaces and most cloud dev environments include Compose docker compose version # Forward the published ports (e.g. 8080) through your platform's port forwarding ``` **Cloud-Specific Notes:** - GitHub Codespaces ships Docker and Compose preinstalled. - Remember to forward published ports so you can reach the app in your browser.

🧙‍♂️ Chapter 1: The Compose File - One Stack, One Command

A single YAML file replaces a wall of docker run flags. Learn its shape and you can describe any stack.

⚔️ Skills You’ll Forge in This Chapter

  • The anatomy of a compose.yaml file
  • Defining a service from a pulled image and from a local build
  • The core lifecycle commands: up, ps, logs, down

🏗️ Your First Compose File

A Compose file is a map of services (each becomes one or more containers), plus optional top-level networks and volumes. Here is a minimal two-service stack: a web app that talks to Redis.

app.py - a tiny Flask app that counts visits in Redis:

import os
from flask import Flask
import redis

app = Flask(__name__)
cache = redis.Redis(host=os.environ.get("REDIS_HOST", "redis"), port=6379)

@app.route("/")
def hello():
    count = cache.incr("hits")
    return f"Hello from Compose! This page has been viewed {count} times.\n"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Dockerfile - to build the web service:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]

requirements.txt:

flask
redis

compose.yaml - the conductor’s score:

services:
  web:
    build: .                 # build the image from the local Dockerfile
    ports:
      - "8080:5000"          # host 8080 -> container 5000
    environment:
      REDIS_HOST: redis      # match the service name below
    depends_on:
      - redis

  redis:
    image: redis:7-alpine    # pulled, not built

Bring the whole thing up:

# Build images as needed and start everything in the background
docker compose up -d

# See the services and their status
docker compose ps

# Follow the logs from all services (Ctrl-C to stop following)
docker compose logs -f

# Reload http://localhost:8080 a few times and watch the counter climb

# Stop and remove the containers, networks (keep named volumes)
docker compose down

One command started an app and a database, networked them, and published a port. That is the entire promise of Compose.

🔍 Knowledge Check: The Compose File

  • Which service is built locally, and which is pulled from a registry?
  • What does "8080:5000" map, and which side is the host?
  • What does docker compose down remove, and what does it keep by default?

⚡ Quick Wins and Checkpoints

  • Stack is up: docker compose ps shows both web and redis running
  • Counter works: Reloading the page increments the visit count
  • Clean teardown: docker compose down removed the containers cleanly

🧙‍♂️ Chapter 2: Networks, Service Discovery, and Volumes

How did the web service reach Redis using just the hostname redis? And why didn’t your data vanish? Two of Compose’s quiet superpowers: automatic networking and named volumes.

⚔️ Skills You’ll Forge in This Chapter

  • Service discovery by name on the default Compose network
  • Defining custom networks to segment traffic
  • Persisting data with named volumes

🏗️ Networking and Service Discovery

Compose puts every service on a shared, private network and registers each service name as a DNS hostname. That is why redis.Redis(host="redis") just works - redis resolves to the Redis container’s IP automatically. You never hard-code IP addresses.

You can also define custom networks to isolate tiers, so that, for example, the database is only reachable by the backend and not by anything else:

services:
  web:
    build: .
    ports:
      - "8080:5000"
    networks:
      - frontend
      - backend
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    networks:
      - backend            # only on backend; nothing on frontend can reach it

networks:
  frontend:
  backend:

🏗️ Persisting Data with Volumes

A container’s writable layer is ephemeral - delete the container and its data is gone. A named volume is storage managed by Docker that lives independently of any container, so your database survives restarts, rebuilds, and down/up cycles. Compare two ways to attach storage:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db-data:/var/lib/postgresql/data   # named volume: durable, Docker-managed
      - ./init:/docker-entrypoint-initdb.d  # bind mount: maps a host folder in

volumes:
  db-data:                                  # declares the named volume
  • A named volume (db-data) is the right tool for database data: durable and portable.
  • A bind mount (./init:...) maps a host directory straight into the container - perfect for live-reloading source code during development.
# Bring the stack up, then prove persistence:
docker compose up -d
docker compose down          # containers gone...
docker compose up -d         # ...but db-data volume is reattached, data intact

# List and inspect volumes
docker volume ls
docker compose down -v       # the -v flag DOES delete named volumes — use with care

🔍 Knowledge Check: Networking & Volumes

  • Why can the web service use redis as a hostname without any IP configuration?
  • What is the difference between a named volume and a bind mount?
  • Which command removes named volumes, and why is it dangerous?

🧙‍♂️ Chapter 3: Configuration, Startup Order, and a Full Stack

Production-shaped stacks need configuration that lives outside the file, dependencies that start in order, and the discipline to keep secrets out of version control.

⚔️ Skills You’ll Forge in This Chapter

  • Externalizing configuration with environment variables and an .env file
  • Controlling startup order with depends_on and health checks
  • Assembling a complete three-service application

🏗️ Environment Variables and the .env File

Hard-coding values in compose.yaml is fine for harmless defaults, but secrets and per-environment settings belong in an .env file that you never commit. Compose loads .env from the project directory automatically and substitutes ${VAR} references.

.env (add it to .gitignore):

POSTGRES_PASSWORD=super-secret-change-me
APP_PORT=8080

compose.yaml referencing it:

services:
  web:
    build: .
    ports:
      - "${APP_PORT}:5000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy   # wait until db is healthy, not just started

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  db-data:

depends_on alone only controls start order - it does not wait for a service to be ready. Pairing it with condition: service_healthy and a healthcheck makes the web service wait until Postgres can actually accept connections, eliminating a whole class of flaky-startup bugs.

🏗️ The Full Stack in Action

# Validate and view the fully-resolved configuration (great for debugging)
docker compose config

# Start everything; web waits for db to be healthy
docker compose up -d

# Scale a stateless service to three replicas behind the same network
docker compose up -d --scale web=3

# Tear down, removing volumes too for a truly clean slate
docker compose down -v

You now have a reproducible, version-controlled, multi-service environment that any teammate can launch with a single command - the foundation every CI/CD pipeline and orchestration platform builds upon.

🔍 Knowledge Check: Config & Startup

  • Why should the .env file be listed in .gitignore?
  • What does depends_on guarantee, and what does it NOT guarantee on its own?
  • How does condition: service_healthy change startup behavior?

🎮 Mastery Challenges

🟢 Novice Challenge: Two-Service Stack

Objective: Reproduce the web + Redis stack from Chapter 1 and confirm the visit counter increments.

Requirements:

  • A compose.yaml with a built web service and a pulled redis service
  • The web service reaches Redis by service name
  • docker compose up -d starts both

Validation: Reloading http://localhost:8080 increases the counter.

🟡 Intermediate Challenge: Add a Persistent Database

Objective: Replace (or add) a Postgres service with a named volume and prove the data survives a teardown.

Requirements:

  • A db service using postgres with a named volume
  • Credentials supplied through an .env file, not hard-coded
  • Demonstrate down then up keeps the data

Validation: Write a row, run docker compose down (without -v), up again, and the row is still there.

🔴 Advanced Challenge: Health-Gated Three-Service App

Objective: Build a web + db + cache stack where the web service waits for the database to be healthy before starting.

Requirements:

  • Three services on appropriate networks
  • A healthcheck on the database and condition: service_healthy on the web service
  • All configuration via .env; docker compose config resolves cleanly

Validation: Stopping then starting the stack never produces a “database not ready” error from the web service.

🏆 Quest Rewards & Achievements

🎖️ Badges Earned:

  • 🏆 Stack Conductor - You orchestrated a multi-container app with a single command
  • 🔗 Service Weaver - You connected services over networks and persisted data with volumes

🛠️ Skills Unlocked:

  • Multi-Container Orchestration - Coordinate many containers as one application
  • Declarative Stack Configuration - Describe infrastructure as a versioned file

🔓 Unlocked Quests:

  • CI/CD Pipeline Basics - Run these stacks automatically on every commit
  • Container Registries Deep Dive - Ship your stack’s images to production

📊 Progression Points: +50 XP

🗺️ Next Steps in Your Journey

Continue the Main Story:

  • 🎯 Level 0101 - CI/CD & DevOps - Automate building and deploying your containerized stacks

Explore Side Adventures:

Character Class Recommendations

💻 Software Developer: Advance toward CI/CD pipelines for your stacks
🏗️ System Engineer: Study networks, health checks, and resource limits in depth
🛡️ Security Specialist: Explore secrets management and network segmentation

📚 Resources

Official Documentation

Community Resources

Learning Materials

🤝 Quest Completion Checklist

  • ✅ Completed all primary objectives
  • ✅ Started a multi-service stack with a single command
  • ✅ Answered all knowledge check questions
  • ✅ Completed at least one mastery challenge
  • ✅ Explored the resource library
  • ✅ Identified your next quest in the journey

🕸️ Knowledge Graph

Structured wiki-links connect this quest to the IT-Journey knowledge graph. Open the Obsidian Graph View to explore connections.

Level hub: [[Level 0100 - Frontend & Containers]] Overworld: [[🏰 Overworld - Master Quest Map]] Requires: [[Docker Container Fundamentals: Master Isolation & Portability for DevOps]] Obsidian docs: [[Obsidian Knowledge Graph and Wiki Links]]

🎁 Rewards

50 XP

Badges

  • 🏆 Stack Conductor - Orchestrated a multi-container app with a single command
  • 🔗 Service Weaver - Connected services over networks and persisted data with volumes

Skills unlocked

  • 🛠️ Multi-Container Orchestration
  • 🧩 Declarative Stack Configuration

Features unlocked

  • Readiness for the CI/CD & DevOps quest line at Level 0101

🕸️ Quest Network

graph TD loading(["Loading quest graph…"])