Docker Compose: A Beginner-Friendly Guide with Real-Time Examples

Anusha KothaAnusha Kotha
9 min read

Introduction

Docker Compose is a handy tool that helps you run and manage applications made of multiple containers. Instead of starting each container one by one, you can use a single docker-compose.yml file to define and start everything with just one command. This guide will walk you through Docker Compose in a simple way, with real examples to help you understand and use it confidently in your DevOps journey.

What is Docker Compose?

Docker Compose lets you define and run multi-container applications. Think of it like this: instead of running several docker run commands for each part of your app (like the database, backend, and frontend), you just write them all in one file (docker-compose.yml) and start everything at once.

Why Should You Use Docker Compose?

  • Manages multiple containers easily:

    Define all your services (like webserver, database, etc.,) in one file and run them together.

  • Improves workflow

    You can simply setup or tear down your development environment with one command .

  • Automatic Networking

    Docker compose links your containers automatically, so they can talk to each other easily.

  • Works across environments

    Use the same setup on your local machine, staging, or production without changes.

Key Parts of a docker-compose.yml File

  • version

    Defines which version of the compose file format you’re using.

  • services

    Lists all the containers (like frontend, backend, database) that makes your app.

  • networks

    Defines how your containers communicate with each other

  • volumes

    Used to store data outside your containers, so it’s not lost when containers are restarted.

  • configs

    Lets you add external configuration files. Keeping configs separate makes your containers cleaner and more reusable.

Installing Docker Compose

Docker Compose comes pre-installed with Docker Desktop on Windows and macOS.

For Linux users, you'll need to install it separately using the following command:

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

Common Docker Compose Commands

  • To execute the compose file
docker-compose up -d
  • To Built the Docker-compse file
docker-compose build
  • To remove the containers
docker-compose down
  • To show the configurations of the compose file
docker-compose config
  • To show the images of the file
docker-compose images
  • To stop the containers
docker-compose stop
  • To show the log details of the file
docker-compose logs
  • To pause the containers
docker-compose pause
  • To unpause the containers
docker-compose unpause
  • To see the containers of the compose file
docker-compose ps

Let’s Write a Basic Docker Compose File to Understand How It Works

To get started, create a file named docker-compose.yaml (or docker-compose.yml). This file will define your application's services, networks, and volumes in a simple format.

Here’s how to begin:

vim docker-compose.yaml

This file will be the blueprint for running all the containers your application needs. Let’s move ahead and write a basic example inside it next.

version: "3"
services:
  webapp1:
    container_name: container_name
    build: <your-Dockerfile-Path>
    ports:
      - "host_port:container_port"

  webapp2:
    container_name: container_name
    build: <your-Dockerfile-Path>
    ports:
      - "host_port:container_port"

We created a Docker Compose file that defines two services (webapp1 and webapp2).

Each service:

  • Has its own container name

  • Builds its image from a Dockerfile (you need to provide the path)

  • Exposes a port, so you can access the app from your browser or Postman

👉 We told Docker to build and run two separate web apps (or containers) using one file, and expose them on different ports.

This setup is useful when you're running multiple apps or microservices together.

Key Notes:

  • Replace <your-Dockerfile-Path> with the actual path (like "./webapp").

  • Make sure container_name is unique for each service.

  • host_port:container_port — for example, "8080:80" means access app on port 8080 of your machine.

Example compose file to create multiple services

NOTE:

The application was built for learning purposes using Docker Compose and a 3-tier architecture.
And the code (like backend and frontend) were generated with the help of ChatGPT, and then customized for this project. As a DevOps learner, this kind of project helped me understand real-world container setups using Docker Compose.
I encourage others to do the same with tools like ChatGPT!

Clone the repo to get the source code

https://github.com/AnushaKotha98/compose.git

Now go to folder

cd compose/paytm

you will find 3 folders: bus-tickets, movie-tickets, train-tickets

lets write the compose file with docker-compose.yml

---
version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"

  bus:
    container_name: bus-container
    build: ./bus-tickets
    ports:
      - "8082:80"

  train:
    container_name: train-container
    build: ./train-tickets
    ports:
      - "8083:80"

this is the basic compose file for deploying three services

Build the docker file

docker-compose build

Start Services

docker compose up -d

This cmd is used to start services defined in a docker-compose.yml file in detached mode (-d), meaning the containers run in the background.

Check Status:

Lets check the containers

The command:

docker-compose ps

is used to list the containers that were started by docker-compose. It shows:

  • Container Name

  • Command

  • State (Up/Exited)

  • Ports

Now lets check the output for 3 services:

Access the Services

  • Movie Service: http://<your-server-ip>:8081

  • Bus Service: http://<your-server-ip>:8082

  • Train Service: http://<your-server-ip>:8083

(If running locally: use localhost or <your-server-ip>)

output-1:

output-2:

output-3:

Volumes in Docker Compose File

In the earlier example, only containers and images were defined in the Docker Compose file. Now, let’s extend it by using volumes, which allow us to persist and share data between the host and containers.

Docker Compose File with Volumes

---
version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"
    volumes:
      - movie_volume:/usr/share/nginx/html:ro

  bus:
    container_name: bus-container
    build: ./bus-tickets
    ports:
      - "8082:80"
    volumes:
      - bus_volume:/usr/share/nginx/html:ro

  train:
    container_name: train-container
    build: ./train-tickets
    ports:
      - "8083:80"
    volumes:
      - train_volume:/usr/share/nginx/html:ro

volumes:
  movie_volume:
  bus_volume:
  train_volume:

Explanation:

  • Each service (movie, bus, train) maps a volume (e.g., movie_volume) to a directory inside the container (/usr/share/nginx/html).

  • The :ro at the end makes it read-only inside the container.

  • The volumes: section at the bottom defines named volumes for Docker to manage.

Create Containers with Volumes

Run the following command to build and start the containers in detached mode:

docker-compose up -d

✅ This will build the images (if not already built), create the containers, and mount the specified volumes.

Check Active Containers and Volumes

Check the running containers:

docker-compose ps

List all Docker volumes:

docker volume ls

Modify Volume Content

If you update the data inside the Docker volume, the changes will be reflected in your application output immediately (if the volume is mounted correctly).

in my output-1: i have Hollywood movie like avengers, now i am changing to Tollywood movies like RRR, KGF, Familyman, Hanu-Man.

let’s modify the HTML content in the movie_volume:

vim /var/lib/docker/volumes/movie_volume/_data/index.html

Now refresh your browser on http://localhost:8081 and you’ll see the updated Tollywood movie list.

✅ Summary

  • Volumes allow data persistence outside the container.

  • Changes to volume content reflect in real-time (no need to rebuild images).

Ideal for serving static files or sharing data across containers.

🌐 Networks in Docker Compose File

When a Docker Compose file is executed, Docker automatically creates a default network and connects all services to it.
However, if you want to isolate services from each other and provide controlled communication, you can define custom networks for each service.

Docker Compose File with Separate Networks

version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"
    volumes:
      - movie_volume:/usr/share/nginx/html:ro
    networks:
      - movie_network

  bus:
    container_name: bus-container
    build: ./bus-tickets
    ports:
      - "8082:80"
    volumes:
      - bus_volume:/usr/share/nginx/html:ro
    networks:
      - bus_network

  train:
    container_name: train-container
    build: ./train-tickets
    ports:
      - "8083:80"
    volumes:
      - train_volume:/usr/share/nginx/html:ro
    networks:
      - train_network

# Declare volumes used by the services
volumes:
  movie_volume:
  bus_volume:
  train_volume:

# Declare custom networks for each service
networks:
  movie_network:
    driver: bridge
  bus_network:
    driver: bridge
  train_network:
    driver: bridge

Explanation

  • Each service (movie, bus, train) is now connected to its own isolated network.

  • Networks are defined under the networks: section with the bridge driver (default for user-defined networks).

  • This setup restricts communication between services, unless explicitly allowed.

Recreate Containers with Networks

Run the following command to recreate the containers with their custom networks:

docker-compose up -d

Verify Networks

List all Docker networks:

docker network ls

You can inspect a specific container’s network settings:

docker inspect movie-container

Environment Variables in Docker Compose:

Using environment variables in Docker Compose adds flexibility and reusability to your configurations. It allows you to avoid hardcoding sensitive or environment-specific values (like ports, database URLs, or credentials).

Here are three common ways to use environment variables in a Docker Compose file:

1. Using a .env File

Create a hidden .env file in the same directory as your docker-compose.yml. Compose will automatically load variables from this file.

vim .env

Add environment variables:

DB_HOST=mysqldb
DB_PORT=3306

Now reference these variables in your Compose file:

version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"
    volumes:
      - movie_volume:/usr/share/nginx/html:ro
    networks:
      - movie_network
    environment:
      - DB_SERVER=${DB_HOST}
      - DATABASE_PORT=${DB_PORT}

➡️ Run the containers:

docker-compose up -d

➡️ Inside the container, verify the environment variables:

docker exec -it movie-container bash
printenv

✅ You should see: DB_SERVER=mysqldb, DATABASE_PORT=3306 - like in the above screenshot

2. Defining Environment Variables Directly in docker-compose.yml

You can define environment variables inline within the Compose file using the environment section:

version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"
    volumes:
      - movie_volume:/usr/share/nginx/html:ro
    networks:
      - movie_network
    environment:
      - DB_ENDPOINT=https://mysqldb.com
      - DB_USER=root

No external files are needed — values are hardcoded here. This is simple but less flexible for switching environments.

3. Using Shell Environment Variables

If environment variables are already set in your host terminal, Docker Compose can pull them in using ${VAR_NAME} syntax.

First, export the variables in your terminal:

export DB_NAME=anusha
export DB_PORT=3306

Then, in your docker-compose.yml file:

version: "3"
services:
  movie:
    container_name: movie-container
    build: ./movie-tickets
    ports:
      - "8081:80"
    volumes:
      - movie_volume:/usr/share/nginx/html:ro
    networks:
      - movie_network
    environment:
      - DB_NAME=${DB_NAME}
      - DB_PORT=${DB_PORT}

Lets use docker-compose up -d command to recreate the container.

containers inside will hold the values DB_NAME=anusha & DB_PORT=3306 . Now this time compose file get the values from docker host which we export.

Conclusion

Docker Compose is a powerful tool for managing multi-container applications. Environment variables make your configuration files:

  • Reusable

  • Secure

  • Flexible

By mastering different ways of using environment variables, you're a step closer to handling real-world DevOps tasks and cloud deployments.

💖 Support & Motivation

If you found this guide helpful for interviews or your learning journey, give it a 💖 — 10 times if you can! 😄
Also, feel free to leave a comment — your support helps create more hands-on DevOps content. 🚀

👉 Stay tuned ❤️❤️❤️❤️❤️

0
Subscribe to my newsletter

Read articles from Anusha Kotha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anusha Kotha
Anusha Kotha