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

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 ❤️❤️❤️❤️❤️
Subscribe to my newsletter
Read articles from Anusha Kotha directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
