>_
EngineeringNotes
Back to Docker Modules

Docker Compose

Orchestrate multi-container applications with ease. One file, one command.

Docker Compose is a tool designed to help you define and run multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. Then, with a single command, you can create and start all the services from your configuration.

Why do we need it?

Imagine you have 5 microservices, a database, and a cache. Do you really want to run 7 different docker run commands every time you start work?

😫 The Manual Way

docker network create my_network
docker volume create my_data
docker run -d --net my_network ... mongo
docker run -d -p 3000:3000 ... node-app

+ 5 more commands... hope you don't make a typo!

😀 The Compose Way

YAML
docker-compose.yml
Define everything once.
$ docker-compose up

One command to rule them all. Networks, volumes, and services created instantly in the correct order.

Running with Compose

Standard Startclick to copy
docker-compose up
Custom Config Fileclick to copy
docker-compose -f docker-compose-development.yml up

The Real World Scenario

Most tutorials show you `docker run mongo` (pulling an existing image). but in real work, you clone a repository containing source code for a Frontend, Backend, and Database. Here is the evolution of setup:

1. Manual Installation
  • Install Node.js locally
  • Clone the repo
  • npm install
  • Start DB locally (download postgres)
  • Configure .env file
  • npx prisma migrate
  • npx prisma generate
  • npm run build
  • npm run start
Pain Level: Extreme 😫
2. Docker (No Compose)
  • Install Node.js
  • Install Docker
  • Start Postgres Container
  • docker run -d -p 5432:5432 ...
  • Build App Image
  • docker build -t app .
  • Start App Container
  • docker run -p 3000:3000 app
Pain Level: Moderate 😐
3. Docker Compose
  • Install Docker
  • $ docker-compose up
  • (Handles Build, DB Start, Networks, Env, Migration automatically)
Pain Level: Zero �

The Solution: A Real World Compose File

This is exactly how you would define the setup for the scenario above. Notice how we link the user_app to the postgres service.

docker-compose.yml
services:
  postgres:
    image: postgres
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: mysecretpassword
      POSTGRES_DB: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data

  user_app:
    build:
      context: ./
      dockerfile: Dockerfile     # <-- Uses the Prisma Dockerfile
    environment:
      DATABASE_URL: postgresql://postgres:mysecretpassword@postgres:5432/postgresdb
    ports:
      - 4000:4000
    depends_on:
      - postgres

volumes:
  postgres_data:

🪄 The Magic Catch: Automatic Networking

You might have noticed something missing: We defined two services (`postgres` and `user_app`), but we never created a Network! How do they talk to each other?

Fact: Docker Compose automatically creates a shared network for all services in the file.

Result: Services can communicate using their service names as hostnames.

DATABASE_URL=postgresql://postgres:mysecretpassword@postgres:5432/postgresdb

(See? We use "postgres" instead of "localhost" or an IP address!)

?
The Developer's Dilemma

"Why use Docker in Dev? `npm run dev` is faster!"

Common Confusion: "If I change code, do I need to rebuild the image every time? That sounds slow!"

The Secret: You DO NOT rebuild every time. We use Volumes (Bind Mounts). Docker mirrors your local code folder into the container. When you save a file on your machine, it updates inside the container instantly, triggering hot-reload just like local dev.

1. Consistency

Everyone on the team uses the exact same Node/Python/DB versions. No "works on my machine" bugs.

2. Isolation

Don't pollute your laptop with 50 versions of Mongo, Redis, and Postgres. Delete container = Clean machine.

3. Production Parity

Your specific dev environment mimics the Linux server environment you'll deploy to.

The Verdict: When to use what?

For Small Projects

(e.g., Simple Node.js app, Personal Portfolio, Learning)

  • Go Native (`npm run dev`)
  • Faster startup (no virtualization overhead)
  • 👌Simple debugging (direct console access)

For Large Projects

(e.g., Microservices, Team Projects, Complex DBs)

  • Must use Docker
  • 🛡️Prevents "Dependency Hell" (Node v14 vs v18)
  • 🤝Instant Onboarding for new team members