DiyMediaServer
Featured image of post Master the Basics Dockers Compose

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’re 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.

By the end of 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?

A Docker Compose file is a 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. Docker does the rest.

Why Use Docker Compose?

Docker Compose makes container management sane. Here’s why you want it:

  • Easy multi-container setup – Instead of running a wall of docker run commands, you define everything in one file.

  • Portability – Share your docker-compose.yml and anyone can replicate your setup.

  • Easier management – Start, stop, or restart all your containers with a single command.

  • Environment variables – Configure your setup using a .env file.

I’ll break down my own docker-compose.yml down. First, a quick word on what’s actually happening under the hood.

What Is Orchestration?

Orchestration is a fancy word for automating how different parts of your system work together.

Think of it like a movie production. You’ve got a pile of moving parts that all have to land at the same time:

  • The director keeps everything on schedule.

  • The actors perform their roles based on the script.

  • The camera crew captures the right angles.

  • The editors put it all together in post-production.

If each person had to be hand-walked through every cue, it would be chaos. Instead, they follow a plan, and the whole thing runs without constant micromanagement.

Docker Compose works the same way. You define the services once, and Docker handles the rest.

YAML Formatting Rules (Read This or Nothing Will Work)

Before you crack open a docker-compose.yml, you need to understand YAML formatting. One stray space or tab will break the whole file. Unlike JSON or XML, YAML lives and dies on indentation.

YAML Rules You Must Follow

  • Use spaces, not tabs. Indentation must be done using spaces. Tabs are not allowed.

  • Consistent indentation. Use the same number of spaces per level (2 or 4 is standard).

  • 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 bare, but wrap them in double quotes "" if they contain special characters.

  • Boolean values (true, false) and numbers don’t need quotes. Write them as-is.

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 docker-compose.yml that runs Radarr, Sonarr, Prowlarr, and SABnzbd, the core tools for an automated media server. Follow this pattern and you can drop in other services without much fuss.

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

Alright, let’s pick this apart.

1. Defining Services

The services: block lists every container you want to run. Each service is a separate Docker container.

  • radarr and sonarr are the service names.
  • Each service runs a specific Docker image (lscr.io/linuxserver/radarr, etc.).
  • container_name sets a custom name for the running container.

2. Environment Variables

Instead of hardcoding values, the Compose file pulls from an .env file. That means you can change a port or a path without touching the Compose file at all.

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

Tweak the .env file and your whole stack picks up the change on the next docker compose up.

3. Port Mapping

Each container has ports mapped like this:

ports:
  - "${RADARR_PORT}:7878"

Port 7878 inside the container (where Radarr listens) is exposed on your host at ${RADARR_PORT} (set in the .env file).

4. Volume Mounts

Volumes persist data between container restarts. Skip them and you lose your settings every time the container cycles.

volumes:
  - ${CONFIG_PATH}/radarr:/config
  - ${DOWNLOADS_PATH}:/downloads
  - ${MEDIA_PATH}/Movies:/movies

5. Restart Policy

The restart: unless-stopped line tells Docker to restart the container automatically unless you manually stop it. Reboot the host, the stack comes back on its own.

6. Depends On

The depends_on keyword sets the startup order between containers. It guarantees one container starts before another, but it does NOT wait for the dependency to be fully ready. Only that the process has launched.

Confused about .env files? See my Master the Basics - Docker’s .env Files post for the full breakdown.

Running the Docker Compose File

Once your docker-compose.yml and .env files are ready, here’s the workflow:

1. Navigate to the folder containing your Compose file

cd /docker

2. Start your containers in the background

docker compose up -d

-d runs the containers in detached mode, so they keep humming along in the background instead of holding your terminal hostage.

3. Check running containers

docker ps

4. Stop the containers

docker compose down

This stops and removes the containers but keeps your data intact, because your volumes live on the host filesystem.

Wrapping It Up

You can now spin up a full multi-container stack with one command. That’s the whole point of Compose.

From here, dig into Docker networks, named volumes, and healthcheck: blocks. Those are the next layers worth learning. But the file you’ve got is enough to run a real media server today.

Drop the file in /docker, run docker compose up -d, and you’re off.

Was this useful?

Last updated on May 20, 2026 06:56 MDT