Guía10 min

Docker Bake para un agente IA: un archivo, varios targets, cero CLI eterno

Resumen

Bake de Docker Buildx declara el build en HCL: group, target, inherits y matrix. Un docker buildx bake construye webhook y worker en paralelo; --print antes de CI; --load local y --push al registry. Distinto de cache de capas y de compose watch. Docs Bake, 7 sep 2026.

Docker
Archivo Bake con grupos y targets que construyen webhook y worker de un agente en paralelo

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

Bake es la capa declarativa de Docker Buildx: en vez de un docker build con veinte flags, dejas la receta en un archivo y docker buildx bake la ejecuta. Para un agente IA con webhook, worker y a veces un job de migrate, eso significa un archivo, varios targets, una invocación. Los targets del grupo corren en paralelo. Fuentes oficiales Docker consultadas el 7 de septiembre de 2026.

No es cache de capas (eso vive en el Dockerfile y en --cache-from/--cache-to). No es Compose watch (eso recarga un servicio en desarrollo). Bake orquesta qué se construye, con qué tags, plataformas y outputs.

El archivo, no la CLI

HCL es el formato preferido. Bake también lee JSON y un Compose con build:. Si no pasas --file, busca en este orden: compose.yaml, compose.yml, docker-compose.yml, docker-compose.yaml, docker-bake.json, docker-bake.hcl, y al final los docker-bake.override.*. Si hay varios, los fusiona. En el merge, tags, platforms, output, dockerfile y cache-to los gana el último archivo; labels se mezclan.

Eso importa si tu repo ya tiene compose.yaml para el VPS y añades docker-bake.hcl para CI: el HCL pisa los tags del Compose. Compruébalo con --print antes de empujar.

variable "TAG" {
  default = "dev"
}

group "default" {
  targets = ["webhook", "worker"]
}

target "webhook" {
  context    = "./webhook"
  dockerfile = "Dockerfile"
  args = {
    NODE_VERSION = "22"
  }
  tags = ["ghcr.io/org/agente-webhook:${TAG}"]
}

target "worker" {
  context    = "./worker"
  dockerfile = "Dockerfile"
  tags = ["ghcr.io/org/agente-worker:${TAG}"]
}

docker buildx bake sin argumentos construye el grupo default (o el target llamado default). docker buildx bake worker construye solo el worker. Comillas en wildcards: docker buildx bake "agente-*"; sin comillas el shell expande archivos.

--print antes de CI

Bake evalúa variables, herencias y matrices antes de construir. --print imprime el JSON resuelto: context, dockerfile, args, tags, platforms. Si TAG sale latest y no lo querías, lo ves aquí, no en el registry.

Overrides útiles:

PalancaQué haceCuándo
--printResuelve y no construyeSiempre, local y en PR
--set webhook.tags=ghcr.io/org/agente-webhook:shaPisa un atributoTag de commit en Actions
--loadoutput=type=docker (condicional)Probar la imagen en el daemon local
--pushoutput=type=registry (condicional)CI que publica
--allow fs.read=../srcEntitlement de filesystemContext fuera del cwd del bake file

--load y --push son atajos. Un target con output explícito a type=local no se “carga” al daemon porque sí: el output del archivo manda salvo que lo pises con --set.

Si el context apunta a ../src, Bake no lee fuera del directorio de trabajo a menos que pases --allow fs.read=../src (o fs.read=*). El default es restrictivo a propósito.

inherits: DRY sin copiar flags

Un target puede heredar de otros. El último de la lista inherits gana en conflicto.

target "_base" {
  args = {
    NODE_VERSION = "22"
  }
  dockerfile = "Dockerfile"
}

target "webhook" {
  inherits = ["_base"]
  context  = "./webhook"
  tags     = ["ghcr.io/org/agente-webhook:${TAG}"]
}

target "webhook-release" {
  inherits  = ["webhook"]
  platforms = ["linux/amd64", "linux/arm64"]
}

El patrón _base (guion bajo) es convención: no lo metes en group "default". Release añade plataformas; dev se queda en la nativa del builder. Para un agente en Fly o un VPS amd64, no pagues el emulador arm64 en cada PR.

Contexts nombrados (contexts = { alpine = "docker-image://alpine:3.21" } o baseapp = "target:base") existen para Dockerfiles que no puedes fusionar. Si puedes, un multi-stage en un solo archivo es más simple.

Grupos Bake que disparan webhook y worker en paralelo con un target de validación aparte

Matrix de Bake ≠ matrix de GitHub Actions

Bake puede forkar un target:

target "runtime" {
  name = "agente-${svc}"
  matrix = {
    svc = ["webhook", "worker"]
  }
  context    = "./${svc}"
  dockerfile = "Dockerfile"
  tags       = ["ghcr.io/org/agente-${svc}:${TAG}"]
}

--print runtime enseña agente-webhook y agente-worker. Varios ejes multiplican combinaciones; mapas en el matrix evitan el producto cartesiano que no quieres (webhook-1.0 + worker-2.0, no las cuatro mezclas).

Eso no sustituye strategy.matrix de Actions. GHA paraleliza jobs (lint / test / deploy). Bake paraleliza builds en un builder. Úsalos juntos: un job bake default, no un job por imagen a menos que el builder no dé abasto. Guía de matrix de workflows: strategy.matrix para agentes.

CI: bake-action y call = "check"

Docker publica docker/bake-action. El check de Build no pide un input especial: defines un target que hereda el build y pone call = "check".

target "webhook" {
  dockerfile = "Dockerfile"
  tags       = ["ghcr.io/org/agente-webhook:${TAG}"]
}

target "validate" {
  inherits = ["webhook"]
  call     = "check"
}

En el workflow: docker/setup-buildx-action, luego docker/bake-action con targets: validate y, si pasa, targets: default + push: true. --print en el job de PR; push solo en main. El pipeline genérico de Actions sigue en CI/CD del agente.

No pongas migrate en default. Un grupo tools o un target migrate que nadie invoca en el deploy automático evita que un bake de prod corra un job de esquema. El mismo criterio que profiles de Compose: lo opt-in no se dispara solo.

Qué no hace Bake (y qué sí debes pinchar)

ConfusiónRealidad
Bake cachea mejor que docker buildEl cache es BuildKit. Bake solo declara cache-from / cache-to si los pones
tags = [":latest"] en defaultPin digest o tag de git; :latest es el mismo anti-patrón de pin de imagen
Un docker build por servicio en el READMEEl README apunta a docker buildx bake --print y a bake default
Context COPY . con el bake file dentroSigue aplicando .dockerignore
Secretos en argstarget.secret + RUN --mount=type=secret; el valor no va al historial de capas

target.secret declara ids (type = "env" o type = "file"). El Dockerfile los monta. --set default.secret.aws=env=AWS_CREDENTIALS cambia la fuente sin reescribir el HCL.

Checklist

  1. Un docker-bake.hcl con group "default" = servicios de runtime, no tools.
  2. docker buildx bake --print en local y en el job de PR.
  3. inherits para flags comunes; matrix solo si las variantes son reales.
  4. --load para dogfood local; --push solo en CI con registry auth.
  5. Target validate con call = "check" antes del bake que publica.
  6. Contexts fuera del cwd: --allow fs.read=<path>, nunca * en prod.
  7. Tags con git sha o semver; no solo latest.

FAQ

¿Compose no alcanza? Compose describe runtime (redes, depends_on, restart). Bake describe builds. Puedes alimentar Bake con el mismo YAML, pero el HCL te da inherits, matrix, --print y entitlements. Si el agente ya vive en Compose en el VPS, deja Compose para up y Bake para CI.

¿Un target por stage del Dockerfile? No. Stages (target = "runner") son del Dockerfile. Targets de Bake son invocaciones. Un target webhook puede poner target = "runner" si el Dockerfile es multi-stage.

¿Puedo bakear tests que exportan a disco? Sí: output = ["type=local,dest=build/tests"]. Eso pide --allow fs.write=build/tests si el dest queda fuera del cwd.

El curso gratis arranca el agente; esta guía cubre cómo construirlo sin un one-liner de 14 flags: instalar el agente.

Matriz de targets Bake resuelta a webhook y worker con tags de git, no latest