Docker Compose: Multi-Container Apps Made Simple

Stop running docker commands one by one. Learn how to define your entire app stack in a single YAML file with Docker Compose.

Docker Compose: Multi-Container Apps Made Simple
πŸ“§

Get weekly IT guides

Join 5,000+ IT professionals

Subscribe Free

Running docker run is fine for one container. But what if your app needs a database? And a cache? And a frontend? Do you really want to run 4 commands in a specific order every time you restart your laptop?

Enter Docker Compose.

Introduction

Docker Compose is a tool that lets you define and run multi-container Docker applications. You create a single file called docker-compose.yml, define everything your app needs (containers, networks, volumes), and start it all with one command.

What You'll Learn
  • What docker-compose.yml looks like
  • How to define services (apps, databases)
  • Networking magic (how containers talk to each other)
  • Key commands: up, down, logs
Mental Model: The Orchestra Conductor
  • Docker containers are individual musicians (violin, cello, drums).
  • Docker Compose is the conductor and the sheet music.
  • It tells everyone: β€œYou sit here,” β€œYou start playing only after the drums start,” β€œYou use this instrument.”
  • One flick of the baton (docker compose up) and the whole orchestra starts in sync.

The docker-compose.yml File

This is where the magic happens. It uses YAML (Yet Another Markup Language), which relies on indentation.

Structure of docker-compose.yml showing version, services, and volumes keys
Anatomy of a docker-compose.yml file. Services, Networks, and Volumes.

A Real Example: Ghost Blog + MySQL

Here is a full stack for a Ghost blog. It needs the Ghost app AND a MySQL database.

version: '3.8'

services:
  ghost-app:
    image: ghost:latest
    ports:
      - "8080:2368"
    environment:
      - database__client=mysql
      - database__connection__host=db
      - database__connection__user=root
      - database__connection__password=secret
      - database__connection__database=ghost
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=ghost
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:
Magic Feature: Service Discovery

Notice database__connection__host=db? Docker Compose automatically creates a network where containers can reach each other by their service name. We named the database service db, so the app can just connect to hostname db. No IP addresses needed!


Essential Commands

Once you have your docker-compose.yml file, you only need these commands:

Start Everything

docker compose up

Start all containers in the foreground (shows all logs)

beginner
docker compose up -d

Start in 'detached' mode (background) - The most common way

beginner

Check Status

docker compose ps

List all containers managed by this Compose file and their status

beginner
docker compose logs -f

Follow the logs of all services (Ctrl+C to stop following)

beginner
docker compose logs -f ghost-app

Follow logs for just one specific service

beginner

Stop and Clean Up

docker compose stop

Stop the containers (preserves them)

beginner
docker compose down

Stop and REMOVE containers and networks

beginner
docker compose down -v

WARNING: Also removes volumes (deletes your database data!)

beginner

Key Concepts Explained

1. Services

A β€œservice” is just a fancy name for a container in the context of your app. In the example above, ghost-app and db are services.

2. Ports ("Host:Container")

"8080:2368" means β€œForward port 8080 on my laptop to port 2368 inside the container.” You open localhost:8080 in your browser.

3. Environment Variables

Inject configuration like passwords, API keys, or debug flags without changing the image code.

4. Volumes

volumes: - db-data:/var/lib/mysql This maps a persistent storage area to the database folder. Even if you destroy the db container, the data in db-data survives. When you restart, your blog posts are still there.

5. depends_on

Tells Docker to start the db before the ghost-app.

Important: depends_on β‰  'Ready to Accept Connections'

depends_on only waits for the container to start, not for the database inside it to be fully ready.

Your app may still crash on startup if it tries to connect before MySQL finishes initializing (~2-5 seconds).

Fix: Add retry logic in your app, or use a health-check with depends_on: condition: service_healthy.


Hands-On Challenge

  1. Create a folder my-blog and enter it.
  2. Create a file named docker-compose.yml and paste the Ghost example code above.
  3. Run docker compose up -d.
  4. Open http://localhost:8080 in your browser. You should see the Ghost blog!
  5. Run docker compose down to clean up.

Key Takeaways

  • One File: Define your whole stack (app, db, cache) in docker-compose.yml.
  • One Command: docker compose up -d launches everything.
  • Networking: Containers talk to each other by service name (e.g., db), not IP.
  • Persistence: Use Volumes to keep database data safe even if containers crash.
  • Production: Docker Compose is great for dev and small servers. For massive scale, you’d upgrade to Kubernetes (which uses similar YAML concepts).
🧠

Test Your Knowledge

Take a quick 5-question quiz to check your understanding.

πŸ“§

Get weekly IT guides

Join 5,000+ IT professionals

Subscribe Free
Type to start searching...