Simplifying Multi-Container Development with Docker Compose

Simplifying Multi-Container Development with Docker Compose In the last blog post, we built a real-world, multi-container application using Docker. However, managing multiple containers manually with long docker run commands can become repetitive, error-prone, and hard to maintain—especially in teams or CI/CD environments. This is where Docker Compose comes in. In this post, we’ll explore what Docker Compose is, what it isn’t, how it simplifies development, and how to use it to manage a full stack application with a MongoDB database, Node.js backend, and React frontend. What is Docker Compose? Docker Compose is a tool designed to help you define and manage multi-container Docker applications. It uses a YAML file (docker-compose.yml) to configure application services, networks, volumes, and environment variables. With a single command, you can build, start, and run your entire application stack. Benefits of Docker Compose: Manage multiple containers as a single service Reproducible and consistent development environments Simplifies networking and volume management Easier to share configurations with teams or deploy to staging What Docker Compose is NOT It's important to understand what Docker Compose doesn't do: It does not replace Dockerfiles — you still need Dockerfiles to build custom images. It does not replace Docker containers or images — it simply manages them. It is not intended for multi-host orchestration (use Kubernetes or Docker Swarm for that). Installing Docker Compose (Linux) If you are using macOS or Windows, Docker Compose is included with Docker Desktop. For Linux: sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose docker-compose --version More details: https://docs.docker.com/compose/install/ Practice Resource Defining Our Application in docker-compose.yml We’ll build a basic full-stack application with: MongoDB database Node.js backend React frontend version: "3.8" services: mongodb: image: mongo container_name: mongodb volumes: - data:/data/db env_file: - ./env/mongo.env backend: build: context: ./backend dockerfile: Dockerfile container_name: backend ports: - '80:80' volumes: - logs:/app/logs - ./backend:/app - ./backend/node_modules:/app/node_modules env_file: - ./env/backend.env depends_on: - mongodb frontend: build: context: ./frontend dockerfile: Dockerfile container_name: frontend ports: - '3000:3000' volumes: - ./frontend/src:/app/src - ./frontend/node_modules:/app/node_modules env_file: - ./env/frontend.env depends_on: - backend stdin_open: true tty: true volumes: data: logs: Breaking Down the Services 1. MongoDB Service Instead of running: docker run --name mongodb \ -e MONGO_INITDB_ROOT_USERNAME=max \ -e MONGO_INITDB_ROOT_PASSWORD=secret \ -v data:/data/db \ --rm -d --network goals-net mongo In Compose: mongodb: image: mongo container_name: mongodb volumes: - data:/data/db env_file: - ./env/mongo.env Example mongo.env: MONGO_INITDB_ROOT_USERNAME=Mayank MONGO_INITDB_ROOT_PASSWORD=Gupta 2. Backend Service Instead of manually running build and run commands: cd backend docker build -t goals-node . docker run --name goals-backend --rm -d -p 80:80 --network goals-net goals-node In Compose: backend: build: context: ./backend dockerfile: Dockerfile container_name: backend ports: - '80:80' volumes: - logs:/app/logs - ./backend:/app - ./backend/node_modules:/app/node_modules env_file: - ./env/backend.env depends_on: - mongodb This also supports live code updates using bind mounts. 3. Frontend Service Manually: cd frontend docker build -t goals-react . docker run --name goals-frontend --rm -d -p 3000:3000 -it goals-react With Compose: frontend: build: context: ./frontend dockerfile: Dockerfile container_name: frontend ports: - '3000:3000' volumes: - ./frontend/src:/app/src - ./frontend/node_modules:/app/node_modules env_file: - ./env/frontend.env depends_on: - backend stdin_open: true tty: true Running the Application Start all containers: docker-compose up Detached mode: docker-compose up -d Stop and remove containers: docker-compose down Stop and remove containers and volumes: docker-compose down -v Rebuild images before starting: docker-compose up --build Advantages of Using Docker

May 4, 2025 - 18:41
 0
Simplifying Multi-Container Development with Docker Compose

Simplifying Multi-Container Development with Docker Compose

In the last blog post, we built a real-world, multi-container application using Docker. However, managing multiple containers manually with long docker run commands can become repetitive, error-prone, and hard to maintain—especially in teams or CI/CD environments.

This is where Docker Compose comes in. In this post, we’ll explore what Docker Compose is, what it isn’t, how it simplifies development, and how to use it to manage a full stack application with a MongoDB database, Node.js backend, and React frontend.

What is Docker Compose?

Docker Compose is a tool designed to help you define and manage multi-container Docker applications. It uses a YAML file (docker-compose.yml) to configure application services, networks, volumes, and environment variables. With a single command, you can build, start, and run your entire application stack.

Benefits of Docker Compose:

  • Manage multiple containers as a single service
  • Reproducible and consistent development environments
  • Simplifies networking and volume management
  • Easier to share configurations with teams or deploy to staging

What Docker Compose is NOT

It's important to understand what Docker Compose doesn't do:

  • It does not replace Dockerfiles — you still need Dockerfiles to build custom images.
  • It does not replace Docker containers or images — it simply manages them.
  • It is not intended for multi-host orchestration (use Kubernetes or Docker Swarm for that).

Installing Docker Compose (Linux)

If you are using macOS or Windows, Docker Compose is included with Docker Desktop.

For Linux:

sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
docker-compose --version

More details: https://docs.docker.com/compose/install/

Practice Resource

Defining Our Application in docker-compose.yml

We’ll build a basic full-stack application with:

  • MongoDB database
  • Node.js backend
  • React frontend
version: "3.8"

services:
  mongodb:
    image: mongo
    container_name: mongodb
    volumes:
      - data:/data/db
    env_file:
      - ./env/mongo.env

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: backend
    ports:
      - '80:80'
    volumes:
      - logs:/app/logs
      - ./backend:/app
      - ./backend/node_modules:/app/node_modules
    env_file: 
      - ./env/backend.env
    depends_on:
      - mongodb

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: frontend
    ports:
      - '3000:3000'
    volumes:
      - ./frontend/src:/app/src
      - ./frontend/node_modules:/app/node_modules
    env_file:
      - ./env/frontend.env
    depends_on:
      - backend
    stdin_open: true
    tty: true

volumes:
  data:
  logs:

Breaking Down the Services

1. MongoDB Service

Instead of running:

docker run --name mongodb \
  -e MONGO_INITDB_ROOT_USERNAME=max \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  -v data:/data/db \
  --rm -d --network goals-net mongo

In Compose:

mongodb:
  image: mongo
  container_name: mongodb
  volumes:
    - data:/data/db
  env_file:
    - ./env/mongo.env

Example mongo.env:

MONGO_INITDB_ROOT_USERNAME=Mayank
MONGO_INITDB_ROOT_PASSWORD=Gupta

2. Backend Service

Instead of manually running build and run commands:

cd backend
docker build -t goals-node .
docker run --name goals-backend --rm -d -p 80:80 --network goals-net goals-node

In Compose:

backend:
  build:
    context: ./backend
    dockerfile: Dockerfile
  container_name: backend
  ports:
    - '80:80'
  volumes:
    - logs:/app/logs
    - ./backend:/app
    - ./backend/node_modules:/app/node_modules
  env_file:
    - ./env/backend.env
  depends_on:
    - mongodb

This also supports live code updates using bind mounts.

3. Frontend Service

Manually:

cd frontend
docker build -t goals-react .
docker run --name goals-frontend --rm -d -p 3000:3000 -it goals-react

With Compose:

frontend:
  build:
    context: ./frontend
    dockerfile: Dockerfile
  container_name: frontend
  ports:
    - '3000:3000'
  volumes:
    - ./frontend/src:/app/src
    - ./frontend/node_modules:/app/node_modules
  env_file:
    - ./env/frontend.env
  depends_on:
    - backend
  stdin_open: true
  tty: true

Running the Application

Start all containers:

docker-compose up

Detached mode:

docker-compose up -d

Stop and remove containers:

docker-compose down

Stop and remove containers and volumes:

docker-compose down -v

Rebuild images before starting:

docker-compose up --build

Advantages of Using Docker Compose

  • One command to run everything
  • Clear declaration of services and dependencies
  • Easily version-controlled with your codebase
  • Enables quick testing and CI integration

Conclusion

Docker Compose helps us streamline multi-container application development by reducing complexity and enabling declarative service definitions. It's ideal for local development, prototyping, and small-scale deployments.

In upcoming blogs, we’ll cover how to use Docker Compose for CI/CD, working with environment-specific files, and Docker Compose for production scenarios.

Explore the source and more projects on my Dev.to profile. If you haven’t checked the first part yet, read it here.