Si tu docker-compose.yml tiene más de 15 servicios —como cualquier homelab que lleve un par de años creciendo— probablemente te duele mantenerlo. Cada nuevo servicio copia los mismos bloques de logging, restart policy, redes, y recursos. Cuando decides cambiar el driver de logging de json-file a local, toca editar 12 servicios uno por uno. Y en algún momento alguien se olvida de uno y los logs no rotan.
YAML anchors, aliases, y extension blocks (x-) resuelven esto. Son una característica del lenguaje YAML que Docker Compose entiende nativamente, sin plugins, sin herramientas externas, sin nada más que sintaxis YAML estándar. Esta guía cubre cómo usarlos en tu homelab con ejemplos reales que puedes copiar y adaptar.
YAML Anchors and Aliases — The Foundation for Reusable Compose Config
YAML tiene un mecanismo nativo para definir un bloque de configuración una vez y reutilizarlo en varios lugares: los anchors (marcados con &nombre) y los aliases (marcados con *nombre).
|
|
El anchor &logging marca un bloque YAML completo. El alias *logging lo inserta en el lugar donde se usa. Docker Compose lo resuelve antes de validar el archivo, así que es como si hubieras escrito el bloque completo en cada servicio.
La regla de oro: mira tu compose file actual. Cualquier bloque que aparezca igual en tres o más servicios es candidato a ser un anchor.
x- Extension Blocks for Structured Reusable Compose Fragments
Docker Compose ignora silenciosamente cualquier llave de nivel superior que comience con x-. Esto las convierte en el vehículo ideal para agrupar anchors de forma legible y predecible.
En lugar de esparcir definiciones &logging, &restart y &resource por todo el archivo, las agrupas en una sección x- al inicio:
|
|
Beneficio inmediato: todas las configuraciones reutilizables están en un mismo lugar, al inicio del archivo. Cambias el driver de logging una vez y se propaga a todos los servicios que usan *logging.
Anchors and Merge Key («) for Service Composition
Los aliases simples (*logging) funcionan para bloques individuales, pero cuando cada servicio necesita combinar logging + restart + resources + redes, escribir *logging, *restart, *limits dentro de cada servicio sigue siendo verboso. Aquí entra el merge key de YAML (<<).
El merge key fusiona uno o varios anchors en un mapa, permitiendo sobreescribir campos específicos por servicio:
|
|
El orden importa: los campos definidos después del merge key sobreescriben los que vienen del anchor. En el ejemplo, worker hereda *limits del defaults (512M) pero lo sobreescribe con 1G.
Real-World Homelab Example — DRY Monitoring Stack with Compose Anchors
Veamos un ejemplo completo. Un stack de monitoreo con Grafana, Prometheus, Loki, y Alloy, usando los patrones anteriores:
|
|
Sin anchors, este mismo archivo tendría cuatro bloques de logging:, cuatro bloques de restart:, cuatro bloques de deploy:resources:, y cuatro bloques de networks:. Con los anchors, la sección x- centraliza todo y cada servicio declara solo lo que le es específico: image, container_name, ports, volumes, environment, y command.
Para iniciar el stack:
|
|
Common Docker Compose YAML Pitfalls and Limitations
Los anchors son poderosos, pero tienen limitaciones que debes conocer para evitar bugs difíciles de depurar.
Anchors No Pueden Mergear Secuencias (Listas)
El merge key << solo funciona con mapas (YAML mappings/dictionaries). No puedes mergear listas. Esto es relevante porque en Compose hay campos como depends_on, ports, volumes, y networks que son listas.
|
|
Solución: usa el alias directamente (*ports), no el merge key. Si necesitas combinar puertos de un anchor con puertos específicos del servicio, tendrás que escribirlos todos en el servicio o usar un anchor que ya contenga todo.
Anchors Son Referencias, No Copias
Un anchor YAML es una referencia, no una copia. Si defines un anchor de environment y un servicio lo modifica después del alias, el anchor original NO se modifica —pero ten cuidado con objetos mutables compartidos.
En la práctica, para Compose esto no es problema porque los values son escalares (strings, números) o mapas planos. Nunca estamos mutando objetos después de creados.
Orden de Resolución del Merge Key
Cuando usas <<: *defaults seguido de campos propios del servicio, el orden es:
- Se aplica el merge key primero (todo lo que viene de
*defaults) - Se aplican los campos explícitos del servicio después
Esto significa que puedes sobreescribir cualquier campo del defaults:
|
|
No Puedes Usar « Dentro de Bloques Anidados
El merge key solo funciona en el nivel donde lo colocas. Si *defaults contiene deploy: con recursos dentro, no puedes hacer un <<: dentro de deploy para mezclar recursos del defaults con recursos extra. El merge es plano.
Solución: si necesitas combinar, define el bloque completo en el servicio y omite el anchor correspondiente, o crea variantes del anchor (por ejemplo, *limits-default y *limits-large).
Resumen: Docker Compose DRY Patterns for Your Homelab
Tres patrones para aplicar hoy mismo:
-
x-blocks: Define
x-logging,x-restart,x-resourcesal inicio de tu compose file. Agrupa toda la configuración reusable en un solo lugar visible. -
Defaults composition: Crea un
x-defaultscon<<: [*restart, *logging, *limits]y reutiliza*defaultsen todos los servicios. Sobreescribe por servicio cuando sea necesario. -
Override on demand: Los anchors no son rígidos — cada servicio puede heredar defaults y cambiar cualquier campo. Usa esto para servicios con requisitos especiales de memoria o redes.
El patrón más impactante con el que puedes empezar hoy: agrega un bloque x-logging: &logging con driver: "local" y max-size: "10m", luego úsalo en todos tus servicios con logging: *logging. Es un cambio de una línea por archivo que evita que los logs de tus contenedores se coman el disco del host.
|
|
El comando docker compose config resuelve todos los anchors y muestra el archivo final expandido — útil para depurar si algo no se aplica como esperas.