Master the Basics Dockers Compose
A Beginner’s Guide to YAML and Container Orchestration

So, you’ve heard about Docker and the magic of containers, but the words “YAML” and “Compose file” sound like something out of a sci-fi movie? Don’t worry. You are in the right place. If you’re new to Docker and wondering how to use a docker-compose.yml file to manage your containers, I’ve got you covered.
When you finish this guide, you’ll know what a Docker Compose file is, how to use it, and why it makes running multiple Docker containers easier.
What Is a Docker Compose File?
At its core, a Docker Compose file is just a simple YAML file (usually named docker-compose.yml
) that tells Docker how to run multiple containers together.
Think of it like a recipe. Instead of manually starting each container, setting its configurations, and linking them together one by one, you write it all down in this file, and Docker does the rest.
Why Use Docker Compose?
Docker Compose simplifies container management. Here’s why you should use it:
-
Easy multi-container setup – Instead of running multiple
docker run
commands, you define everything in one file. -
Portability – Share your
docker-compose.yml
file to allow anyone to replicate your setup. -
Easier management – Start, stop, or restart all your containers with a single command.
-
Environment variables – Configure your setup using a simple
.env
file.
Now that you know why it’s useful, I will break down my docker-compose.yml
file later in this post.
What Is Orchestration?
Orchestration is just a fancy word for automating how different parts of your system work together.
Think of it like a movie production. You have multiple moving parts that all need to work together seamlessly:
-
The director ensures everything happens at the right time.
-
The actors perform their roles based on the script.
-
The camera crew captures the right angles.
-
The editors put everything together in post-production.
If each person had to be manually told what to do every single time, it would be chaos. Instead, they follow a predefined plan, and everything runs smoothly without constant micromanagement.
Docker Compose works the same way. Instead of manually starting each container, configuring it, and linking everything together, Docker automates the process so your services interact as expected.
YAML Formatting Rules (Read This or Nothing Will Work!)
Before diving into the actual docker-compose.yml
, you must understand YAML formatting because one small mistake in spacing or indentation will break everything. Unlike JSON or XML, YAML relies heavily on proper indentation and formatting.
YAML Rules You Must Follow:
-
Use spaces, not tabs – Indentation must be done using spaces. Tabs are not allowed.
-
Consistent indentation – Always use the same number of spaces per level (2 or 4 spaces are common).
-
Key-value pairs are separated by colons – Example:
container_name: radarr
. -
Lists use dashes (-) – Each item in a list starts with
-
. -
Strings don’t need quotes (but sometimes they do) – Strings are usually fine without quotes, but use double quotes
""
if the string contains special characters. -
Boolean values (
true
,false
) and numbers don’t need quotes – Just write them as they are.
Example of Correct vs. Incorrect YAML Formatting
✅ Correct YAML
services:
radarr:
container_name: radarr
restart: unless-stopped
❌ Incorrect YAML (Tabs used instead of spaces)
services:
radarr: # ❌ Tabs used (invalid)
container_name: radarr
restart: unless-stopped
❌ Incorrect YAML (Inconsistent indentation)
services:
radarr:
container_name: radarr
restart: unless-stopped # ❌ Indentation is off (invalid)
Breaking Down a Docker Compose File
Here’s a real-world example of a docker-compose.yml
file that sets up Radarr, Sonarr, Prowlarr, and SABnzbd essential tools for an automated media server. Following this example, you should be able to easily add other services.
services:
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
env_file: .env
ports:
- "${RADARR_PORT}:7878"
volumes:
- ${CONFIG_PATH}/radarr:/config
- ${DOWNLOADS_PATH}:/downloads
- ${MEDIA_PATH}/Movies:/movies
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
restart: unless-stopped
depends_on:
- sabnzbd
- prowlarr
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
env_file: .env
ports:
- "${SONARR_PORT}:8989"
volumes:
- ${CONFIG_PATH}/sonarr:/config
- ${DOWNLOADS_PATH}:/downloads
- ${MEDIA_PATH}/TV:/tv
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
restart: unless-stopped
depends_on:
- sabnzbd
- prowlarr
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
env_file: .env
ports:
- "${PROWLARR_PORT}:9696"
volumes:
- ${CONFIG_PATH}/prowlarr:/config
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
restart: unless-stopped
sabnzbd:
image: lscr.io/linuxserver/sabnzbd:latest
container_name: sabnzbd
env_file: .env
ports:
- "${SABNZBD_PORT}:8080"
volumes:
- ${CONFIG_PATH}/sabnzbd:/config
- ${DOWNLOADS_PATH}:/downloads
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
restart: unless-stopped
Now let’s break it down piece by piece.
1. Defining Services
The services:
section lists all the containers you want to run. Each service is essentially a separate Docker container.
radarr
andsonarr
are the names of each service.- Each service runs a specific Docker image (
lscr.io/linuxserver/radarr
, etc.). - The container_name specifies a custom name for each running container.
2. Environment Variables
Instead of hardcoding values, we use an .env
file to store environment variables. This makes it easy to change settings without modifying the Compose file.
Example .env
file:
RADARR_PORT=7878
SONARR_PORT=8989
PROWLARR_PORT=9696
SABNZBD_PORT=8080
CONFIG_PATH=/path/to/config
DOWNLOADS_PATH=/path/to/downloads
MEDIA_PATH=/path/to/media
PUID=1000
PGID=1001
TZ=America/Denver
This way, you can just change the .env
file without editing the docker-compose.yml
every time.
3. Port Mapping
Each container has ports mapped like this:
ports:
- "${RADARR_PORT}:7878"
This means port 7878 inside the container (where Radarr runs) is accessible on your local machine at ${RADARR_PORT}
(set in the .env
file).
4. Volume Mounts
Volumes persist data between container restarts. Without them, you’d lose your settings when restarting.
volumes:
- ${CONFIG_PATH}/radarr:/config
- ${DOWNLOADS_PATH}:/downloads
- ${MEDIA_PATH}/Movies:/movies
5. Restart Policy
The restart: unless-stopped
line ensures the container will restart automatically unless you manually stop it.
6. Depends On
The depends_on
keyword is used to define startup order between containers. It ensures that certain containers start before others, but it does NOT wait until the dependent container is fully ready, only that it has started.
Confused about .env files? See my Docker .env post for a detailed breakdown.
Running the Docker Compose File
Once your docker-compose.yml
and .env
files are ready, all you have to do is:
1. Navigate to the folder containing your Compose file:
cd /path/to/your/compose/file
2. Start your containers (in the background):
docker compose up -d
-d
runs the containers in detached mode, so they keep running in the background.
3. Check running containers:
docker ps
4. Stopping the containers:
docker compose down
This stops and removes all containers but keeps your data intact.
Wrapping It Up
Congratulations. You now know how to use a Docker Compose YAML file to spin up multiple containers with just one command.
This is just the start. Docker Compose can manage networks and even define dependencies between services. But for now, you have a solid foundation.
Give it a try, and soon you’ll wonder how you ever managed Docker without it.