ADR-0006 · Las migraciones se aplican al arrancar el contenedor¶
- Estado: aceptado
- Decisores: hquintero
- Fecha de la decisión: 2026-08-27
Contexto y problema¶
El deploy necesita aplicar las migraciones de src/migrations/ antes de servir la versión nueva.
Pero la imagen (output: standalone) empaqueta solo lo que el servidor usa: el CLI
payload migrate no viaja en la imagen, así que el job de deploy no tiene con qué ejecutarlo.
Contexto de infraestructura que pesa en la decisión: la base PostgreSQL no vive en el compose — está en una VM dedicada, fuera del host del CMS. El pipeline nunca gestiona el ciclo de vida de la base; solo se conecta.
Opciones consideradas¶
prodMigrationsen el config: Payload aplica las migraciones al inicializar (elegida). Es el mecanismo oficial de Payload para despliegues sin CLI.- Que el runner clone el repo e instale dependencias para correr
payload migratedesde afuera — reinstala medio proyecto en cada deploy, exactamente lo que la imagen evita. - Publicar una segunda imagen «con herramientas» solo para migrar — dos artefactos por versión.
Decisión¶
Opción 1, con una compuerta explícita: prodMigrations se activa solo si
PAYLOAD_MIGRATE_ON_INIT=true, variable que definen los servidores vía compose y nadie más.
La compuerta no es opcional, y la prueba lo demostró: next build corre con
NODE_ENV=production, así que sin la variable de por medio el build local intentaba migrar
contra la base de dev (gestionada por push), detectaba el conflicto y quedaba colgado en un
prompt interactivo — el mismo patrón del push sin TTY.
Verificado el 27/08/2026 contra una base creada vacía: al primer acceso, Payload aplicó las 3
migraciones en orden, construyó el esquema completo (77 tablas) y /admin respondió 200.
Consecuencias¶
Positivas¶
- El paso
migratedesaparece del job de deploy:pull→up -dy el arranque hace el resto. - Si una migración falla, la versión nueva no sirve tráfico de Payload; el error queda en los logs del contenedor.
Negativas / deuda asumida¶
- La credencial de la app necesita DDL. La idea de un usuario de base sin DDL para la app
(protección contra un
next devapuntado a producción) deja de ser posible: quien migra es la app. La protección se muda a la red: el firewall /pg_hba.confde la VM de base acepta conexiones solo desde los hosts del CMS, así una máquina de desarrollo ni siquiera alcanza la base de producción. Esa VM dedicada existe; la regla hay que configurarla (definición pendiente del plan). - La inicialización de Payload es perezosa: ocurre con la primera petición que lo necesita,
no al arrancar el proceso.
/api/healthno la dispara (a propósito). El smoke test del deploy es en dos pasos:/api/health(la app vive) y/admin(fuerza la inicialización → aplica y verifica migraciones). - El modelo asume una sola instancia del CMS por entorno. Dos réplicas arrancando a la vez migrarían en simultáneo; si algún día hay réplicas, esta decisión se revisa.
- Un rollback de imagen vieja sobre esquema ya migrado sigue rigiéndose por la regla del
runbook: revertir el
downa mano o convivir con el esquema adelantado si es compatible.