Docker Compose Made Simple: A Practical Guide with Real Examples

🐳 Understanding Docker Compose With Practical Examples
Your simplified guide to managing multi-container apps
👋 Intro
In the world of Docker, we often run applications in multiple containers—like a web app, a database, a cache, etc. Managing all these manually using docker run
commands becomes hectic. That’s where Docker Compose comes in.
Let’s say we have a simple voting app. It’s made up of several components:
A frontend app (
voting-app
) where users vote between two optionsA backend worker that processes votes
A Redis service to temporarily store votes
A PostgreSQL database to persist final results
A result app (
resulting-app
) to display the results
Each of these runs in a different container. Instead of starting them one-by-one with docker run
, we use Docker Compose to define them all in a single docker-compose.yml
file and bring them up with one command.Docker Compose allows us to define and manage multi-container Docker applications using a simple YAML file.
In this article, I’ll show you practical examples of how to write docker-compose.yml
files. If you're a beginner, don't worry—I’ve kept things simple and straight to the point.
🧩 Starting Simple
Let’s start with the most basic way of running containers without Compose:
docker run pragya/simple-webapp
docker run mongodb
docker run redis:alpine
docker run ansible
But this is too manual, especially if you want to start everything at once.
Now let’s rewrite that with Docker Compose:
services:
web:
image: "pragya/simple-webapp"
database:
image: "mongodb"
messaging:
image: "redis:alpine"
orchestration:
image: "ansible"
To bring up the whole setup, just run:
docker-compose up
Simple, right?
🧱 Building More Realistic Setups
Here’s another setup using individual docker run
commands:
docker run -d --name=redis redis
docker run -d --name=db postgres
docker run -d --name=vote -p 8001:80 voting-app
docker run -d --name=results -p 8002:80 resulting-app
docker run -d --name=worker worker
Now the same thing using Docker Compose:
services:
redis:
image: "redis"
database:
image: "postgres"
vote:
image: "voting-app"
ports:
- "8001:80"
result:
image: "resulting-app"
ports:
- "8002:80"
worker:
image: "worker"
Much cleaner and easier to scale!
🛠️ Building from Source Code
If you don’t have an image pushed to the Docker registry, you can build it locally:
vote:
image: voting-app
build: ./voting-app
Here, Docker will look for a Dockerfile
inside ./voting-app
and build the image automatically.
🔢 Compose File Versions
Let’s look at how Docker Compose evolved across versions.
📄 Without Version
services:
redis:
image: "redis"
database:
image: "postgres"
vote:
image: "voting-app"
ports:
- "8001:80"
links:
- redis
Here, links
was used to connect containers, but it's now outdated.
📄 Version 2
version: "2"
services:
redis:
image: "redis"
database:
image: "postgres"
vote:
image: "voting-app"
ports:
- "8001:80"
depends_on:
- redis
In version 2, we use depends_on
instead of links
. This ensures that Redis starts before the vote container.
📄 Version 3
version: "3"
services:
redis:
image: "redis"
database:
image: "postgres"
vote:
image: "voting-app"
ports:
- "8001:80"
depends_on
is still supported, but Compose version 3 focuses more on Docker Swarm compatibility.
🌐 Docker Compose with Networks
You can define custom networks too:
services:
redis:
image: "redis"
networks:
- back-end
database:
image: "postgres"
networks:
- back-end
vote:
image: "voting-app"
networks:
- front-end
- back-end
result:
image: "resulting-app"
networks:
- front-end
- back-end
networks:
front-end:
back-end:
This setup separates internal services (back-end
) from external-facing services (front-end
) for better architecture and security.
🧪 One More Example for Better Understanding
services:
redis:
image: "redis"
database:
image: "postgres"
vote:
image: "voting-app"
ports:
- "5000:80"
links:
- redis
worker:
image: "worker"
links:
- db
- redis
result:
image: "resulting-app"
ports:
- "5001:80"
links:
- db
You can start everything using:
docker-compose up
🧪 Environment Variables Example
version: "3"
services:
redis:
image: "redis"
database:
image: "postgres:9.4"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
vote:
image: "voting-app"
ports:
- "5000:80"
worker:
image: "worker"
result:
image: "resulting-app"
ports:
- "5001:80"
This is useful when you need to pass credentials or configs securely inside containers.
🔚 Conclusion
Docker Compose helps to define, run, and manage multi-container applications easily with a single docker-compose.yml
file. You’ve seen different versions, examples, and ways to connect services.
If you're just getting started, try converting your manual docker run
commands into a Compose file like above. You’ll feel the power of simplicity.
Subscribe to my newsletter
Read articles from Pragya Saraswat directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
