Developer Guides

Docker Compose Tutorial: Example with Multi-Service Applications

Learn Docker Compose with this hands-on tutorial to create and manage multi-service applications, complete with examples.

5 min read

Docker Compose Tutorial: Example with Multi-Service Applications

Docker Compose simplifies the process of managing multi-service applications by providing a straightforward way to define and orchestrate containerized services in a single YAML file. In this tutorial, you'll learn how to get started with Docker Compose and build a functional example featuring a multi-service application.

Understanding Docker Compose

Docker Compose is a tool designed to define and manage multi-container applications efficiently. At its core, Docker Compose allows you to define the services, networks, and volumes your application needs in a single docker-compose.yaml file. This enables seamless orchestration, letting you launch and manage your application stack with just one command.

Key components of Docker Compose:

  • Services: Define individual containers that make up your application (e.g., a web server, database, or backend service).
  • Networks: Allow services to communicate with each other securely and conveniently.
  • Volumes: Provide persistent storage for data that needs to survive container restarts.

Prerequisites for Docker Compose Implementation

Before diving into the tutorial, ensure the following:

prerequisites

  • Docker is installed and running on your system.
  • Docker Compose is compatible with your Docker version. For Docker Desktop users, Docker Compose is built-in.
  • If you're using macOS or Windows, install [Docker Desktop].
  • Ensure your system is ready to execute Docker and terminal commands.

Setup Steps for a Docker Compose Example

This example demonstrates how to set up a three-tier architecture consisting of a backend API, a frontend application, and a PostgreSQL database.

Step 1: Prepare the Project Directory

Create a clean directory structure for your project.

file tree

project/
├── backend/
│   ├── Dockerfile
│   ├── app.py
│   └── requirements.txt
├── frontend/
│   ├── Dockerfile
│   ├── package.json
│   ├── package-lock.json
│   └── public/
├── .env
├── .gitignore
└── docker-compose.yaml

Step 2: Backend Service Setup

Create the Dockerfile for the backend service inside the backend directory.

dockerfile
# backend/Dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Also, define your Python dependencies in the requirements.txt.

# backend/requirements.txt
fastapi==0.100.0
uvicorn[standard]==0.23.2
psycopg2-binary==2.9.6

Add basic FastAPI code in app.py.

python
# backend/app.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def home():
    return {"message": "Backend service is running!"}

Step 3: Frontend Service Setup

Create the Dockerfile for the frontend service inside the frontend directory.

dockerfile
# frontend/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "start"]

Include a basic package.json for a React app in the frontend directory.

json
{
  "name": "todo-frontend",
  "version": "1.0.0",
  "scripts": {
    "start": "react-scripts start"
  }
}

Step 4: Define docker-compose.yaml

Create a docker-compose.yaml in the project root.

yaml
version: "3.8"

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: todo_db
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "admin"]
      interval: 10s
      retries: 5

  backend:
    build:
      context: ./backend
    ports:
      - "8000:8000"
    environment:
      DB_HOST: db
      DB_USER: admin
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: todo_db
    depends_on:
      db:
        condition: service_healthy

  frontend:
    build:
      context: ./frontend
    ports:
      - "3000:3000"
    depends_on:
      - backend

volumes:
  db_data:

Step 5: Protect Sensitive Data

To securely handle sensitive data like passwords, create a .env file and add it to .gitignore.

# .env
DB_PASSWORD=your_strong_password
# .gitignore
.env

Step 6: Launch the Application

Run the following commands to start the application.

Launch Docker Compose Stack

docker compose up --build
# The stack starts. Services will be built and containers created.

Validating the Example Application Setup

Once the services are running, validate everything as follows:

  1. Check running containers: IT_GUIDES_COMPONENT_3

  2. Access services:

    • Backend API: http://localhost:8000.
    • Frontend UI: http://localhost:3000.
  3. Access logs: IT_GUIDES_COMPONENT_4

  4. Inspect the database: IT_GUIDES_COMPONENT_5


Useful Tips for Secure and Efficient Setup


FAQ

What is Docker Compose best used for?

Docker Compose is ideal for local development and testing of multi-service applications. It simplifies setup by managing all dependencies and services with a single configuration file.

Can Docker Compose be used in production environments?

While Docker Compose is excellent for local development, production-grade setups often require orchestrators like Kubernetes to handle scalability and robust network configurations.

How do you specify database credentials securely in Docker Compose?

Store sensitive data in an environment file (e.g., .env) and ensure the file is excluded from version control using .gitignore. You may also leverage Docker secrets for stricter security in production.


Official reference: Docker Compose documentation.