Docker Compose

What is Docker Compose?

Docker Compose is a tool that makes life easier when you use multiple containers in one project. With Docker Compose, you can run multiple services at once using just one configuration file (docker-compose.yml).

Why Use Docker Compose?

  • Can run many containers with a single command

  • Configuration is cleaner and easier to understand

  • Can use environment variables for flexible settings

  • Easy to scale services



Simple Implementation

We’ll create a simple example: a Node.js app connected to a PostgreSQL database.

Folder Structure:

my_project/
│── docker-compose.yml
│── app/
│   ├── server.js
│   ├── package.json

docker-compose.yml content:

services:
  web:
    image: node:21
    container_name: web_app
    working_dir: /app
    volumes:
      - .:/app # Mount the current folder to /app in the container
    ports:
      - "3000:3000" # hostPort:containerPort
    command: "node server.js"
    depends_on:
      - db

  db:
    image: postgres:16
    container_name: postgres_db
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydatabase
    ports:
      - "5432:5432" # hostPort:containerPort

package.json content:

{
  "name": "web-app",
  "version": "1.0.0",
  "description": "Node.js app with PostgreSQL using Docker Compose",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.3",
    "pg": "^8.11.3"
  }
}

server.js content:

const express = require('express');
const { Client } = require('pg');

const app = express();
const port = 3000;

// Database connection config
const db = new Client({
  user: 'user',
  host: 'db', // Matches the database service name in docker-compose.yml
  database: 'mydatabase',
  password: 'password',
  port: 5432,
});

// Try to connect to the database
db.connect()
  .then(() => console.log('Database connected!'))
  .catch(err => console.error('Database connection failed:', err));

// Main endpoint
app.get('/', async (req, res) => {
  try {
    const result = await db.query('SELECT NOW()');
    res.json({ message: 'Server is running!', time: result.rows[0].now });
  } catch (err) {
    res.status(500).json({ error: 'Failed to fetch time from database' });
  }
});

// Start server
app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

Brief explanation:

  • web service → Runs the Node.js app inside a container and exposes port 3000

  • db service → PostgreSQL with default database settings

  • depends_on → Ensures the database service starts before the web service


Basic Docker Compose commands:

🚀 Start all containers:

docker compose up -d

🛑 Stop all containers:

docker compose down

📜 View container logs:

docker compose logs -f

Scaling with Docker Compose

If you want more than one instance of a certain service, use this command:

docker compose up --scale web=3 -d

This will run 3 instances of the web service, while the database remains a single instance.