docker-compose-sintaxis

Docker Compose File Syntax

  • 4 min

The docker-compose.yml file is the blueprint of your architecture. It’s a text file written in YAML format.

A Docker Compose file has 3 main high-level blocks:

  1. services: (Required) The containers we are going to launch.
  2. volumes: (Optional) The persistent “hard drives” we are going to create.
  3. networks: (Optional) The custom networks to connect everything.

Let’s go through them one by one.

In YAML, indentation is sacred. No curly braces {} or semicolons ; are used. Hierarchy is defined with spaces (usually 2 spaces per level).

Never use tabs. If you mix spaces and tabs, Docker will give you an error and you won’t know why.

The services block

This is where we define our containers. Each element inside services is a container (or a replica of them).

The best way to understand it is by “translating” a docker run command. Imagine this command you already master:

docker run -d \
  --name mi-web \
  -p 8080:80 \
  -v ./html:/usr/share/nginx/html \
  nginx:alpine
Copied!

How would this be written in Compose?

services:
  mi-web:                    # Service name (Will act as internal DNS)
    image: nginx:alpine      # Base image
    container_name: mi-web   # Optional: Forces a specific name
    ports:
      - "8080:80"            # Port list (Host:Container)
    volumes:
      - ./html:/usr/share/nginx/html # Volumes or Bind Mounts
Copied!

Do you see the similarity? It’s the same thing, just much more organized.

Main keys inside a service:

  • image: What image to use (e.g., mysql:5.7).
  • build: If instead of pulling an image, you want to build one from a Dockerfile (we’ll see this later).
  • ports: List of port mappings (HOST:CONTAINER). Important: Always use quotes "80:80" to prevent YAML from interpreting low numbers (like port 60) as base sixty.
  • environment: Environment variables (equivalent to -e).
  • volumes: Disk mapping (equivalent to -v).
  • restart: Restart policy. restart: always means that if the container crashes, Docker will bring it back up. Magic!

The volumes block

If you use a Bind Mount in your services (a path on your PC ./folder:/data), you don’t need to declare anything else.

But if you want to use Managed Volumes (the correct way for Databases), you must declare them at the end of the file as a global resource.

services:
  base-datos:
    image: postgres
    volumes:
      - db-data:/var/lib/postgresql/data  # I use the volume 'db-data'

volumes:
  db-data:  # I declare that this volume exists!
Copied!

If you don’t add the volumes: block at the end, Docker will give you an error saying that the volume db-data is not defined.

The networks block

By default, Docker Compose automatically creates a unique network for your project and puts all services into it.

This means that if you have a web service and a db service, they can ping each other using their names (ping db) without you configuring anything.

But if you want to isolate things (e.g., frontend network and backend network), you can declare them explicitly.

services:
  web:
    image: nginx
    networks:
      - red-frontend

  db:
    image: mysql
    networks:
      - red-backend

networks:
  red-frontend:
  red-backend:
Copied!

Full Example

Let’s put it all together in a real example. A web application that uses a MySQL database and phpMyAdmin to manage it.

version: '3.8'  # Format version

services:
  # Service 1: The Database
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password123
      MYSQL_DATABASE: mi_app
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - backend-net

  # Service 2: The visual manager (phpMyAdmin)
  admin:
    image: phpmyadmin/phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db          # We use the name of the other service
    depends_on:
      - db                  # Waits for 'db' to start before starting this one
    networks:
      - backend-net

volumes:
  mysql-data:               # Persistent volume so the DB doesn't get deleted

networks:
  backend-net:              # Private network
Copied!

What have we just defined?

With this simple text file, we have defined:

  1. Two servers (db and admin).
  2. A private network where they communicate.
  3. A persistent volume for the data.
  4. The password and port configuration.
  5. The startup dependencies (depends_on).

Now, we would just save this as docker-compose.yml and run docker compose up -d. Boom! System deployed.

Don’t upload secrets or passwords

In the example, I used password123. That’s bad, it’s just for the example. If you upload this file to GitHub, you’ve been hacked.

We’ll cover how to handle passwords correctly in the next post.