CodingNic

Containerizing with Docker

Docker Compose

Containerizing with Docker 15 min read

Docker Compose

Objectives

By the end of this lesson, you should be able to:

  • Explain what Docker Compose adds on top of individual docker run commands
  • Write a Compose file running an API together with a database and a cache
  • Use named volumes to persist database data across container restarts

💡 Why this matters: A real application in this track isn’t just one container, it’s an API, a PostgreSQL database (Course 2), and, starting in Module 7 of this course, a Redis cache, all needing to run together, and be able to reach each other, Docker Compose is what coordinates that with one command instead of several.

⚠️ A note on verification: Docker can’t run inside this course’s own sandboxed tooling. The Compose file and commands below reflect Docker Compose’s stable, current, documented behavior. Run this yourself, on your own machine, with Docker installed.

A Compose File

text
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/notes_dev
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=notes_dev
    volumes:
      - db-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  cache:
    image: redis:7
    ports:
      - "6379:6379"

volumes:
  db-data:

Three services, api (built from the local Dockerfile, Lesson 2), db (a stock PostgreSQL image, no custom build needed), and cache (a stock Redis image), each with its own configuration, all defined in one file.

Service Names as Hostnames

DATABASE_URL=postgresql://postgres:postgres@db:5432/notes_dev, db, not localhost, this is the key mechanic Compose provides, every service can reach every other service by its name in the Compose file, on Compose’s own internal network, api’s code doesn’t need to know or care about container IP addresses, db and cache are just hostnames that resolve correctly, automatically.

Named Volumes

db-data:/var/lib/postgresql/data mounts a named volume, managed by Docker, at PostgreSQL’s data directory inside the container. Without it, a database’s data would live only inside the container’s own filesystem, gone the moment that container is removed, a named volume persists independently, surviving a container restart, or even a full rebuild of the db service, exactly what any real database needs.

Running It

bash
docker compose up
text
[+] Running 3/3
 ✔ Container myapp-db-1     Started
 ✔ Container myapp-cache-1  Started
 ✔ Container myapp-api-1    Started

One command, all three services started together, in dependency order (depends_on ensures db and cache start before api), connected on the same internal network, docker compose down stops and removes them, docker compose down -v additionally removes the named volumes, actually deleting the database’s data, useful for a genuinely clean slate, dangerous to run by accident against anything that matters.

Why This Matters for Local Development

Before Compose, running this stack locally meant manually installing PostgreSQL and Redis, or hand-running several separate docker run commands with the networking flags to let them reach each other, matching Compose’s behavior. docker compose up, with a docker-compose.yml file committed to the project, means any developer, on any machine, gets the exact same local stack, running, in seconds, git clone and docker compose up away from a fully working environment.

Try It

  1. Write a Compose file with an api service and a db service, using a named volume for the database’s data.
  2. Confirm the API can actually reach the database, using the service name (db) as the hostname, not localhost.
  3. Stop the stack with docker compose down, start it again with docker compose up, and confirm the database’s data is still there, thanks to the named volume.
  4. Explain, in one or two sentences, why docker compose down -v is a dangerous command to run against anything other than a local, disposable environment.

Recap

  • Docker Compose starts multiple, coordinated containers with one command, each service reachable by the others using its service name as a hostname.
  • Named volumes persist data, a database’s contents, across container restarts and rebuilds, without them, that data is lost the moment a container is removed.
  • A committed Compose file gives every developer on a project the exact same local stack, in seconds, regardless of what’s installed on their own machine.

Next lesson: exercises, containerizing a complete application and running it with Docker Compose end to end.