Manuales+50 XP

Manual de Docker Compose para empezar: de cero a tener tus servicios en un solo archivo

Qué es Docker Compose, cómo se lee un docker-compose.yml, los comandos del día a día, volúmenes, puertos y variables, con ejemplos reales de mi servidor casero.

Iván· Publicado el · 4 minRead in English →

Todos los servicios de mi Raspberry Pi viven en un solo archivo: docker-compose.yml. Con él arranco, paro, actualizo o reconstruyo todo en segundos. Si empiezas con Docker, Compose es lo primero que deberías aprender después del docker run.

Qué problema resuelve

Sin Compose, cada contenedor se arranca con un comando larguísimo:

docker run -d --name gitea --restart unless-stopped \
  -p 3000:3000 -p 2222:22 -v ./gitea:/data \
  -e USER_UID=1000 -e USER_GID=1000 docker.gitea.com/gitea:1.25

Con cinco servicios, te acabas olvidando de qué opciones pusiste. Compose guarda todo eso en un archivo que se lee fácil y se puede versionar con Git.

Anatomía de un docker-compose.yml

services:
  gitea:                                  # nombre del servicio
    image: docker.gitea.com/gitea:1.25    # imagen y versión
    container_name: gitea                 # nombre fijo del contenedor
    restart: unless-stopped               # que arranque solo tras reiniciar
    environment:                          # variables de configuración
      USER_UID: "1000"
      USER_GID: "1000"
    ports:                                # anfitrión:contenedor
      - "3000:3000"
      - "2222:22"
    volumes:                              # dónde se guardan los datos
      - ./gitea:/data

  uptime-kuma:
    image: louislam/uptime-kuma:2
    container_name: uptime-kuma
    restart: unless-stopped
    ports: ["3001:3001"]
    volumes: [./uptime-kuma:/app/data]

Es YAML: la sangría (con espacios, nunca tabuladores) importa. Vamos por partes.

image

La imagen y su etiqueta (versión). Mi consejo: fija la versión (:1.25, :2) en vez de :latest. Con latest, un día haces pull y te instala una versión mayor sin avisar.

restart

  • unless-stopped: arranca solo al encender la máquina, salvo que lo pararas tú. Es el que uso casi siempre.
  • always: arranca siempre, aunque lo pararas.
  • no: nunca solo (útil para tareas que se ejecutan una vez).

ports

Formato anfitrión:contenedor. "8080:80" significa: «lo que llegue al puerto 8080 de mi máquina, mándalo al 80 de dentro del contenedor».

Y un truco de seguridad: si pones una IP delante, solo escucha en esa interfaz:

    ports: ["127.0.0.1:8222:80"]   # solo accesible desde la propia máquina

Así tengo mi gestor de contraseñas: nadie de la red puede llegar directamente.

volumes

Lo más importante de todo. Un contenedor es desechable: si lo borras, todo lo que había dentro se pierde. Los volumes sacan los datos fuera:

  • ./gitea:/data → la carpeta gitea junto al archivo compose se monta en /data dentro. Este tipo es el más fácil de copiar: la copia de seguridad es copiar la carpeta.
  • datos:/data (sin ./) → un volumen con nombre que gestiona Docker. Más limpio, pero hay que saber dónde está para copiarlo.
  • /ruta:/ruta:ro → el :ro lo monta en solo lectura.

environment

Variables que el programa lee para configurarse. Cada imagen documenta las suyas.

Para contraseñas y secretos, no los escribas en el compose (acabarán en Git). Usa un archivo .env al lado, que no subes:

    environment:
      DB_PASSWORD: ${DB_PASSWORD}
# .env (no se sube a Git)
DB_PASSWORD=algo-largo-y-secreto

Los comandos del día a día

Siempre desde la carpeta del docker-compose.yml:

Comando Qué hace
docker compose up -d Crea y arranca todo en segundo plano
docker compose up -d gitea Solo ese servicio
docker compose ps Qué está en marcha
docker compose logs -f gitea Ver los registros en directo (Ctrl+C para salir)
docker compose stop Parar sin borrar
docker compose down Parar y borrar los contenedores (los datos de los volumes se quedan)
docker compose pull Descargar versiones nuevas de las imágenes
docker compose exec gitea sh Abrir una terminal dentro del contenedor

Actualizar un servicio

docker compose pull uptime-kuma
docker compose up -d uptime-kuma

up -d detecta que la imagen ha cambiado y recrea el contenedor con la nueva. Los datos, en su volume, siguen ahí. Después, para liberar espacio de imágenes viejas:

docker image prune

Errores típicos

  • «port is already allocated»: otro programa usa ese puerto. Búscalo con sudo ss -ltnp | grep :PUERTO y cambia el puerto de la izquierda.
  • Permisos en la carpeta de datos: muchos contenedores corren con un usuario concreto (por eso las variables PUID/PGID o USER_UID). Si no puede escribir, revisa quién es el dueño de la carpeta.
  • Tabuladores en el YAML: error de sintaxis. Solo espacios.
  • Olvidar el volume: todo funciona… hasta que recreas el contenedor y los datos desaparecen. Comprueba siempre dónde guarda la imagen sus datos.

Mi forma de organizarlo

~/servicios/
├── docker-compose.yml
├── .env
├── gitea/
├── uptime-kuma/
├── homepage/
└── vaultwarden/

Un archivo, una carpeta por servicio. La copia de seguridad es copiar ~/servicios, y reconstruir el servidor en otra máquina es copiar la carpeta y hacer docker compose up -d.

Mini-quiz

¿Te has quedado con la idea?

Tres preguntas rápidas. Cada acierto suma 10 XP.

  1. En ports: ["8080:80"], ¿qué número es el del ordenador anfitrión?
  2. ¿Qué pasa con los datos si borras un contenedor sin volumen?
  3. ¿Qué hace docker compose up -d?
  • #docker
  • #docker-compose
  • #contenedores
  • #yaml
  • #homelab
Esc