Tu compose.yaml de la lección anterior usa siete claves. La especificación de Compose define más de sesenta, pero no necesitas memorizarlas: la mayoría son la traducción directa de una opción de docker run que ya dominas del módulo 3.

Esta lección es la referencia práctica de un servicio, organizada por bloques y aplicada a Aurora Libros. Al final tendrás los cuatro servicios declarados en un solo fichero. La explicación de cómo interactúan entre sí es la lección 04-04, y las variables de entorno tienen lección propia, la 04-05.

Contenido

  1. Tabla maestra: claves de servicio y su equivalente en docker run
  2. Imagen y construcción: image y build
  3. Identidad: container_name y hostname
  4. Ejecución: command, entrypoint, user, init y el apagado
  5. Red y puertos: ports, expose, networks, alias
  6. Datos: volumes, tmpfs, read_only
  7. Salud y dependencias: healthcheck y depends_on
  8. Recursos y reinicio: restart y deploy.resources
  9. Etiquetas: labels
  10. Bloques de nivel superior: volumes: y networks:
  11. Anclas y referencias YAML para no repetirse
  12. Aurora Libros: los cuatro servicios declarados

  1. Tabla maestra: claves de servicio y su equivalente en docker run

Clave de Compose Opción de docker run Para qué sirve
image argumento final Imagen de la que parte el contenedor
build docker build aparte Construir la imagen desde un Dockerfile
container_name / hostname --name / --hostname Nombre del contenedor y nombre de host interno
command / entrypoint argumentos tras la imagen / --entrypoint Sustituyen al CMD y al ENTRYPOINT de la imagen
user / working_dir -u / -w UID:GID del proceso y directorio de trabajo
init --init Inserta tini como PID 1
stop_signal / stop_grace_period --stop-signal / --stop-timeout Señal de apagado y margen antes de SIGKILL
environment / env_file -e / --env-file Variables dentro del contenedor
ports -p Publicar puertos en el host
expose --expose Documentar puertos internos
networks --network Redes a las que se conecta
volumes -v / --mount Montajes de datos
tmpfs / read_only --tmpfs / --read-only Ficheros en RAM y raíz inmutable
healthcheck --health-* Sonda de salud
depends_on (no existe) Orden de arranque entre servicios
restart --restart Política de reinicio
deploy.resources.limits --memory, --cpus Límites de recursos
labels --label Metadatos
cap_add / cap_drop --cap-add / --cap-drop Capacidades del kernel
extra_hosts --add-host Entradas en /etc/hosts

La única fila sin equivalente es depends_on, y no es casualidad: la relación entre servicios es exactamente lo que docker run no sabe expresar.

  1. Imagen y construcción: image y build

image toma una referencia igual que docker run: repositorio y etiqueta (postgres:16-alpine) o, mejor para producción, un digest inmutable (postgres@sha256:9f3d0e...).

build le dice a Compose que construya la imagen en lugar de descargarla. En forma corta es solo el contexto (build: ./api); en forma larga, todo lo que aprendiste en el módulo 2:

    build:
      context: ./api                    # directorio del contexto de build
      dockerfile: Dockerfile            # relativo al contexto
      args: { NODE_VERSION: "22" }      # valores para las instrucciones ARG
      target: produccion                # etapa concreta en builds multietapa
      cache_from: ["auroralibros/aurora-api:cache"]
    image: auroralibros/aurora-api:1.2.0   # nombre con el que se etiqueta

Cuando aparecen las dos claves juntas, build manda al construir e image da el nombre del resultado. Es la combinación más útil: docker compose build etiqueta la imagen con ese nombre y docker compose push la publica.

Situación Usa Motivo
Servicio de terceros (Postgres, Redis, Nginx) image No tienes su código
Desarrollo de tu propio código build (+ image) Reconstruyes al cambiar
Producción image con etiqueta inmutable Despliegas lo mismo que probaste
CI que construye y publica Ambas build + push con un nombre fijo

Con solo build, si no pones image, Compose etiqueta la imagen como <proyecto>-<servicio>.

  1. Identidad: container_name y hostname

    container_name: aurora-db     # casi siempre: NO lo pongas
    hostname: aurora-db           # nombre que devuelve `hostname` dentro

container_name fija el nombre exacto y desactiva el prefijado de proyecto. Suena cómodo, y por eso mucha gente lo pone; tiene tres inconvenientes serios: impide docker compose up --scale (dos contenedores no pueden llamarse igual), impide levantar dos proyectos del mismo fichero en la misma máquina, y no lo necesitas, porque para hablar entre servicios se usa el nombre del servicio. Ponlo solo cuando algo externo —un script heredado, un agente de monitorización— exija un nombre concreto.

  1. Ejecución: command, entrypoint, user y el apagado

    entrypoint: ["/usr/local/bin/arranque.sh"]     # sustituye al ENTRYPOINT
    command: ["node", "server.js"]                 # sustituye al CMD
    user: "1001:1001"                              # UID:GID sin privilegios
    working_dir: /app
    init: true                                     # tini como PID 1
    stop_signal: SIGQUIT                           # nginx apaga ordenado con esta
    stop_grace_period: 30s                         # margen antes de SIGKILL

command y entrypoint admiten forma de cadena (command: node server.js, que pasa por el shell) y forma de lista (["node", "server.js"], ejecución directa). Prefiere la lista: es la forma exec y garantiza que tu proceso sea PID 1 y reciba SIGTERM, tal como viste en la lección 03-02. init: true inserta un init mínimo como PID 1 que reenvía señales y entierra zombis, útil cuando tu proceso lanza hijos y no los limpia. Y stop_grace_period acepta unidades (10s, 1m30s): para PostgreSQL, que necesita cerrar un checkpoint antes de morir, súbelo a 30 segundos.

  1. Red y puertos: ports, expose, networks, alias

ports publica un puerto del contenedor en el host. Forma corta, "[IP:]hostPort:contenedorPort[/protocolo]":

    ports:
      - "8080:80"                  # todas las interfaces del host
      - "127.0.0.1:5432:5432"      # solo localhost
      - "3000"                     # puerto aleatorio del host → 3000
      - "5514:514/udp"             # protocolo explícito

La forma larga es más verbosa pero explícita: - {target: 80, published: "8080", protocol: tcp, mode: host}, donde target es el puerto interno, published el del host y mode: ingress solo tiene sentido en Swarm.

expose no publica nada: documenta qué puertos escucha el contenedor. Es informativo, porque dentro de una red de usuario todos los puertos entre contenedores ya son accesibles.

networks conecta el servicio a las redes declaradas en el bloque de nivel superior. Sin esta clave, el servicio va a la red default del proyecto.

    networks:
      trasera:
        aliases: [base-de-datos]   # nombre DNS adicional dentro de esa red

Un servicio puede estar en varias redes a la vez —es exactamente lo que hará aurora-api en la lección 04-04— y los aliases le dan nombres DNS extra, útiles para migrar sin tocar el código de los clientes.

  1. Datos: volumes, tmpfs, read_only

La forma corta es la sintaxis origen:destino[:opciones] de docker run -v, y el origen decide el tipo de montaje:

    volumes:
      - aurora-datos:/var/lib/postgresql/data          # volumen con nombre
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro   # bind (empieza por ./)
      - /app/node_modules                              # volumen anónimo

La regla es la del módulo 3: si el origen empieza por . o / es un bind mount; si no, es un volumen con nombre que debe declararse en el bloque volumes: de nivel superior. La forma larga equivale a --mount:

    volumes:
      - type: volume
        source: aurora-datos
        target: /var/lib/postgresql/data
      - type: bind
        source: ./db/init.sql
        target: /docker-entrypoint-initdb.d/init.sql
        read_only: true
      - type: tmpfs
        target: /tmp
        tmpfs: { size: 67108864 }    # 64 MB, en bytes

Una diferencia importante: con la forma corta, si el origen de un bind no existe, Docker crea un directorio vacío; con la forma larga, falla con un error claro y te ahorra el clásico "monté un directorio donde esperaba un fichero".

    read_only: true          # raíz del contenedor inmutable
    tmpfs: [/tmp, /run]      # lo poco que sí necesita escribir

read_only: true con un par de tmpfs es una de las medidas de endurecimiento más baratas que existen; el tema completo es la lección 05-03.

  1. Salud y dependencias: healthcheck y depends_on

El healthcheck de Compose sustituye o complementa al HEALTHCHECK del Dockerfile:

    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/salud"]
      interval: 10s        # cada cuánto se ejecuta
      timeout: 3s          # cuánto espera la respuesta
      retries: 3           # fallos seguidos antes de marcar unhealthy
      start_period: 20s    # gracia inicial: los fallos aquí no cuentan
      start_interval: 2s   # sondeo más frecuente durante start_period

Tres formas de escribir test:

Forma Ejemplo Cómo se ejecuta
CMD ["CMD", "pg_isready", "-U", "aurora"] Directa, sin shell
CMD-SHELL ["CMD-SHELL", "curl -f localhost/salud || exit 1"] Con /bin/sh -c: admite tuberías y ||
Cadena test: pg_isready -U aurora Equivale a CMD-SHELL
Desactivar test: ["NONE"] Anula el HEALTHCHECK de la imagen

start_period es la clave que casi nadie usa y casi todo el mundo necesita: PostgreSQL tarda unos segundos en aceptar conexiones la primera vez, y sin ese margen el contenedor se marca unhealthy antes de haber tenido oportunidad de arrancar.

depends_on en forma cortadepends_on: [aurora-db, aurora-cache]— solo garantiza el orden de arranque: Compose lanza el contenedor de PostgreSQL, espera a que el proceso esté en marcha y acto seguido lanza la API. Pero PostgreSQL tarda cinco segundos más en aceptar conexiones, así que la API arranca contra una base de datos que aún no responde. La forma larga resuelve exactamente eso:

    depends_on:
      aurora-db:
        condition: service_healthy      # espera a que el healthcheck pase
        restart: true                   # reinicia este servicio si la dependencia se recrea
      aurora-cache:
        condition: service_started      # basta con que esté arrancado
      aurora-migraciones:
        condition: service_completed_successfully   # espera a que termine con código 0
Condición Espera a que la dependencia... Requiere
service_started Esté en marcha (equivale a la forma corta) Nada
service_healthy Pase su sonda de salud Un healthcheck definido
service_completed_successfully Termine con código de salida 0 Un servicio de un solo uso

La distinción entre "arrancado" y "listo" es la que separa un compose.yaml que funciona en tu máquina de uno que funciona siempre. Se desarrolla en la lección 04-04.

  1. Recursos y reinicio: restart y deploy.resources

restart acepta los cuatro valores de docker run con el mismo significado: no (por defecto), always, on-failure[:n] y unless-stopped. Los límites, en cambio, viven bajo deploy, un bloque que originalmente era exclusivo de Swarm:

    deploy:
      resources:
        limits:
          memory: 512M       # equivale a --memory 512m
          cpus: "1.0"        # equivale a --cpus 1.0
          pids: 200          # equivale a --pids-limit 200
        reservations:
          memory: 256M       # garantía mínima (equivale a --memory-reservation)
          cpus: "0.25"

Aquí hay una confusión clásica que conviene aclarar: fuera de Swarm, docker compose sí aplica deploy.resources.limits. Lo que se ignora son replicas, placement, update_config y rollback_config, porque no tienen sentido en un solo host. Y recuerda de la lección 03-07 que limits es un techo duro —superar el de memoria significa OOM y código 137— mientras que reservations es una garantía blanda que influye en quién cede memoria bajo presión.

  1. Etiquetas: labels

    labels:
      com.auroralibros.componente: "base-de-datos"
      com.auroralibros.equipo: "plataforma"

Admite también forma de lista (- "clave=valor"). Usa la notación de DNS inverso en las claves para no chocar con las de otras herramientas. Sirven para filtrar (docker ps --filter "label=..."), y muchas herramientas del ecosistema —proxies inversos automáticos, agentes de métricas— se configuran exclusivamente con etiquetas.

  1. Bloques de nivel superior: volumes: y networks:

Todo volumen con nombre que uses en un servicio debe declararse arriba:

volumes:
  aurora-datos:                    # lo más habitual: driver local por defecto

  aurora-copias:
    name: copias-aurora-libros     # nombre exacto, SIN prefijo de proyecto
    labels: { com.auroralibros.retencion: "30d" }

  aurora-compartido:
    external: true                 # ya existe; Compose no lo crea ni lo borra

  aurora-nfs:
    driver: local
    driver_opts:                   # opciones que se pasan al driver
      { type: nfs, o: "addr=10.0.0.20,rw,nfsvers=4", device: ":/exports/aurora" }

external: true es la protección definitiva para datos críticos: Compose asume que el volumen existe y ni siquiera down -v lo toca. Si no existe, el up falla en lugar de crear uno vacío en silencio.

Las redes siguen el mismo patrón:

networks:
  frontal:
    driver: bridge

  trasera:
    driver: bridge
    internal: true                 # sin acceso a Internet ni al exterior
    labels: { com.auroralibros.zona: "datos" }

  externa:
    external: true
    name: aurora-net               # una red creada fuera de Compose

internal: true es la clave que sostiene la segmentación de la lección 04-04: los contenedores de esa red no tienen ruta hacia el exterior, ni el exterior hacia ellos. Una base de datos ahí dentro no puede descargar nada de Internet aunque alguien logre ejecutar código en ella.

  1. Anclas y referencias YAML para no repetirse

Cuando cuatro servicios comparten política de reinicio, logging y etiquetas, copiarlo cuatro veces es pedir que se desincronicen. YAML ofrece anclas (&), referencias (*) y fusión de mapas (<<:), y Compose añade las claves de extensión x-, que ignora al procesar el fichero:

x-comun: &comun                       # define el ancla "comun"
  restart: unless-stopped
  logging:
    driver: json-file
    options: { max-size: "10m", max-file: "3" }

x-sonda-rapida: &sonda-rapida
  { interval: 10s, timeout: 3s, retries: 3, start_period: 20s }

services:
  aurora-cache:
    <<: *comun                        # fusiona todo el contenido del ancla
    image: redis:7-alpine
    healthcheck:
      <<: *sonda-rapida               # las anclas también valen para sub-bloques
      test: ["CMD", "redis-cli", "ping"]

  aurora-web:
    <<: *comun
    image: nginx:alpine
    restart: always                   # las claves locales GANAN a las fusionadas

Dos límites que conviene conocer: las anclas solo funcionan dentro del mismo fichero (no cruzan varios -f), y <<: fusiona al primer nivel, no en profundidad: si el ancla trae labels y el servicio también define labels, el del servicio sustituye al del ancla por completo, no los mezcla. Comprueba siempre el resultado con docker compose config.

  1. Aurora Libros: los cuatro servicios declarados

Con todo lo anterior, este es el fichero completo. Las credenciales siguen escritas a mano —eso se arregla en la lección 04-05— y hay una sola red; la segmentación llega en la 04-04.

# compose.yaml — Aurora Libros S.L.
name: aurora-libros

x-comun: &comun
  restart: unless-stopped
  logging:
    driver: json-file
    options: { max-size: "10m", max-file: "3" }
  labels:
    com.auroralibros.proyecto: "aurora-libros"

x-sonda: &sonda
  { interval: 10s, timeout: 3s, retries: 3, start_period: 20s }

services:

  aurora-db:
    <<: *comun
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: aurora
      POSTGRES_PASSWORD: aurora_secreta
      POSTGRES_DB: aurora_libros
    ports:
      - "127.0.0.1:5432:5432"
    volumes:
      - aurora-datos:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      <<: *sonda
      test: ["CMD-SHELL", "pg_isready -U aurora -d aurora_libros"]
      start_period: 30s          # la primera inicialización tarda más
    stop_grace_period: 30s
    deploy:
      resources:
        limits: { memory: 512M, cpus: "1.0", pids: 200 }
        reservations: { memory: 256M }
    networks: [aurora-net]

  aurora-cache:
    <<: *comun
    image: redis:7-alpine
    command: ["redis-server", "--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"]
    healthcheck:
      <<: *sonda
      test: ["CMD", "redis-cli", "ping"]
    deploy:
      resources:
        limits: { memory: 256M, cpus: "0.5", pids: 100 }
    networks: [aurora-net]

  aurora-api:
    <<: *comun
    build:
      context: ./api
      dockerfile: Dockerfile
    image: auroralibros/aurora-api:1.2.0
    environment:
      PORT: "3000"
      DB_HOST: aurora-db
      DB_USER: aurora
      DB_PASSWORD: aurora_secreta
      DB_NAME: aurora_libros
      REDIS_HOST: aurora-cache
    ports:
      - "3000:3000"          # solo para pruebas directas contra la API
    healthcheck:
      <<: *sonda
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/salud"]
    restart: on-failure:3    # sobrescribe el unless-stopped del ancla
    deploy:
      resources:
        limits: { memory: 256M, cpus: "1.0", pids: 100 }
        reservations: { memory: 128M }
    networks: [aurora-net]

  aurora-web:
    <<: *comun
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./web/index.html:/usr/share/nginx/html/index.html:ro
      - ./web/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    stop_signal: SIGQUIT
    healthcheck:
      <<: *sonda
      test: ["CMD", "wget", "--spider", "-q", "http://localhost/"]
    deploy:
      resources:
        limits: { memory: 128M, cpus: "0.5", pids: 50 }
    networks: [aurora-net]

volumes:
  aurora-datos:

networks:
  aurora-net:
    driver: bridge

Cincuenta líneas de bash imperativo se han convertido en un fichero declarativo, versionable y legible, y de paso ha ganado cosas que el guion no tenía: sondas de salud en los cuatro servicios, rotación de logs y etiquetas coherentes. Valídalo y levántalo:

cd ~/aurora-libros
docker compose config --quiet && docker compose up -d --build
docker compose ps --format "table {{.Service}}\t{{.Status}}"
SERVICE        STATUS
aurora-api     Up 25 seconds (healthy)
aurora-cache   Up 35 seconds (healthy)
aurora-db      Up 35 seconds (healthy)
aurora-web     Up 24 seconds (healthy)

Los cuatro servicios healthy. Que hayan arrancado en el orden correcto es suerte, porque todavía no hay ni un depends_on; eso se arregla en la lección 04-04.

Errores Comunes y Consejos

Poner container_name por costumbre. Rompe el escalado y la coexistencia de proyectos, y no aporta nada: para hablar entre servicios se usa el nombre del servicio.

Usar un volumen con nombre sin declararlo arriba. Compose falla con service refers to undefined volume. Los bind mounts no necesitan declaración; los volúmenes con nombre, sí.

Esperar que depends_on a secas espere a que el servicio esté listo. Solo espera a que arranque. Sin condition: service_healthy seguirás teniendo carreras de arranque.

Definir un healthcheck sin start_period. El servicio se marca unhealthy durante su arranque normal y arrastra a todo lo que dependa de él.

Escribir test: curl -f http://localhost/salud en una imagen Alpine. curl no viene instalado en las imágenes Alpine oficiales; wget sí. Un healthcheck que invoca un binario inexistente falla siempre, y el síntoma —unhealthy sin más pistas— despista mucho.

Consejo: empieza cada servicio nuevo con image, restart y healthcheck, y añade el resto solo cuando lo necesites. Un compose.yaml con claves que nadie sabe por qué están es tan malo como un guion de bash.

Ejercicios

Ejercicio 1. Traduce a un servicio de Compose este comando, sin omitir ninguna opción, y explica dónde va cada una:

docker run -d --name aurora-informes --network aurora-net \
  -u 1001:1001 -w /app -e TZ=Europe/Madrid --read-only --tmpfs /tmp \
  -v aurora-informes-salida:/salida --memory 192m --cpus 0.5 \
  --restart on-failure:2 --stop-timeout 20 \
  --health-cmd 'wget --spider -q http://localhost:4000/salud' --health-interval 15s \
  auroralibros/aurora-informes:0.3.0 node informes.js --diario

Ejercicio 2. El bloque x-comun del fichero de Aurora Libros incluye labels. Añade a aurora-db una etiqueta propia com.auroralibros.componente: "base-de-datos" y comprueba con docker compose config qué pasa con la etiqueta heredada. Explica el resultado y propón una solución.

Ejercicio 3. Declara un volumen aurora-copias que apunte a un volumen externo ya existente llamado respaldos-aurora, móntalo de solo lectura en aurora-db en /respaldos, y demuestra que docker compose down -v no lo borra.

Soluciones

Solución 1.

  aurora-informes:
    image: auroralibros/aurora-informes:0.3.0
    command: ["node", "informes.js", "--diario"]   # todo lo que va tras la imagen
    user: "1001:1001"
    working_dir: /app
    environment: { TZ: Europe/Madrid }
    read_only: true
    tmpfs: [/tmp]
    volumes:
      - aurora-informes-salida:/salida
    restart: on-failure:2
    stop_grace_period: 20s
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:4000/salud"]
      interval: 15s
    deploy:
      resources:
        limits: { memory: 192M, cpus: "0.5" }
    networks: [aurora-net]

--name se omite deliberadamente (el nombre del servicio ya identifica al contenedor), --network pasa a networks, los límites bajan a deploy.resources.limits, --stop-timeout se convierte en stop_grace_period con unidades, y los argumentos posteriores a la imagen se convierten en command en forma de lista.

Solución 2. Añadiendo labels: { com.auroralibros.componente: "base-de-datos" } al servicio aurora-db:

docker compose config | grep -A3 "aurora-db:" | grep -A2 labels
    labels:
      com.auroralibros.componente: base-de-datos

La etiqueta com.auroralibros.proyecto ha desaparecido: <<: fusiona solo al primer nivel, y como el servicio define su propia clave labels, esta sustituye por completo al mapa heredado. La solución es una segunda ancla para el contenido de las etiquetas:

x-etiquetas: &etiquetas
  com.auroralibros.proyecto: "aurora-libros"

services:
  aurora-db:
    <<: *comun
    labels:
      <<: *etiquetas
      com.auroralibros.componente: "base-de-datos"

Ahora config muestra las dos. Regla general: cuando fusiones anclas, verifica siempre con docker compose config; lo que crees que hereda y lo que hereda de verdad no siempre coinciden.

Solución 3. Tras docker volume create respaldos-aurora, añade el montaje a aurora-db y la declaración externa:

    volumes:
      - aurora-copias:/respaldos:ro     # dentro de aurora-db

volumes:
  aurora-datos:
  aurora-copias:
    external: true
    name: respaldos-aurora
docker compose up -d
docker compose exec aurora-db touch /respaldos/prueba   # debe fallar: :ro
docker compose down -v
docker volume ls --format "{{.Name}}" | grep -E "respaldos|aurora-datos"
touch: /respaldos/prueba: Read-only file system
respaldos-aurora

down -v ha borrado aurora-libros_aurora-datos, que era del proyecto, pero respaldos-aurora sigue ahí: al ser external, Compose lo considera prestado y no lo gestiona. Es el mecanismo que debes usar para cualquier volumen cuya pérdida sea inaceptable.

Conclusión

Ya tienes la referencia completa de un servicio de Compose, y sobre todo el mapa mental que la sostiene: casi cada clave es una opción de docker run con otro nombre, y solo depends_on expresa algo que la CLI imperativa no sabía decir. Sabes cuándo usar image y cuándo build —y por qué a menudo las dos juntas—, por qué container_name sobra casi siempre, cómo se traducen command, entrypoint, user, init, stop_signal y stop_grace_period, y las dos formas —corta y larga— de ports y volumes, con el matiz de que la larga falla en lugar de crear un directorio vacío cuando el bind no existe.

Dominas el bloque healthcheck completo, incluido el start_period que evita marcar como enfermo a un servicio que solo está arrancando, y conoces la diferencia entre las tres condiciones de depends_on: service_started, service_healthy y service_completed_successfully. Sabes que restart tiene los mismos cuatro valores de siempre y que deploy.resources.limits sí se aplica fuera de Swarm. Declaras volúmenes y redes en los bloques de nivel superior, blindas los datos críticos con external: true, aíslas con internal: true, y evitas la repetición con anclas x-/&/*/<<: sabiendo que la fusión es superficial. Y tienes el compose.yaml con los cuatro servicios de Aurora Libros declarados.

En la lección siguiente, Comandos de Docker Compose, pasarás del fichero a la CLI: el ciclo de vida completo (up y su reconciliación, down y el peligro de -v, start, stop, restart, pause), la observación (ps, logs, top, stats, events), la diferencia esencial entre exec y run, la construcción y publicación, el escalado local con --scale y por qué choca con los puertos fijos, y la selección de ficheros y proyecto con -f, -p y COMPOSE_FILE.

Docker: De Principiante a Avanzado

Módulo 1: Introducción a Docker

Módulo 2: Trabajando con Imágenes Docker

Módulo 3: Contenedores Docker

Módulo 4: Docker Compose

Módulo 5: Conceptos Avanzados de Docker

Módulo 6: Docker en Producción

Módulo 7: Ecosistema y Herramientas de Docker

© Copyright 2026. Todos los derechos reservados