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

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/
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.