Ir al contenido

Desplegar, paso a paso

Esta página es el recorrido completo, sin dar nada por sabido: qué se toca, en qué orden, desde qué carpeta y en qué plataforma.

Puesta en producción explica cómo se armó la infraestructura una vez. Esta explica cómo se despliega cada cambio.

Y Checklist de despliegue es la que se recorre cada vez: qué verificar en cada ambiente, el respaldo antes de migrar, y las cinco cosas que nunca se hacen contra producción ahora que hay clientes reales cargados.

Tres repositorios separados, tres plataformas, una sola base de datos.

Repo Dónde vive en tu máquina Se despliega en Qué es
api ~/code/aquazaku/api Railway El backend. Habla con Postgres
web ~/code/aquazaku/web Vercel El front. Habla con api, nunca con Postgres
docs ~/code/aquazaku/docs (sitio estático) Esta documentación

Y la base:

Dónde Schema
Producción Supabase public
Preview la MISMA Supabase preview
Local Postgres de tu máquina public de aquazaku_dev

Railway y Vercel despliegan solos al mergear a main. Las migraciones NO.

El startCommand de producción está vacío a propósito: el container solo arranca el servidor. Migrar es un acto separado, deliberado, que hacés vos desde tu máquina.

De ahí salen las dos preguntas de todo despliegue:

  1. ¿Este cambio trae una migración?
  2. Si la trae, ¿el código nuevo aguanta el schema viejo, o al revés?

El caso común. No hay nada que coordinar.

Ventana de terminal
gh pr merge --merge

Railway y Vercel detectan el push a main y redespliegan. Listo.

Caso B — Con migración ADITIVA que el código nuevo NO usa todavía

Sección titulada «Caso B — Con migración ADITIVA que el código nuevo NO usa todavía»

Aditiva quiere decir que solo agrega: una columna nullable, una tabla, un índice. Y la condición que importa es la segunda mitad: que el código que se está desplegando no la lea.

Ahí el orden documentado funciona: mergear primero, migrar después.

Ventana de terminal
gh pr merge --merge
Ventana de terminal
cd ~/code/aquazaku/api
DATABASE_MIGRATION_URL='<cadena de Supabase>' pnpm db:migrate

Caso C — Con migración que RENOMBRA, CONVIERTE o rompe el código viejo

Sección titulada «Caso C — Con migración que RENOMBRA, CONVIERTE o rompe el código viejo»

Acá el orden se invierte, y no es una preferencia: es que el otro orden rompe la planta.

Una migración que renombra nombre a nombre_libre deja al código viejo y al nuevo incompatibles en direcciones opuestas. Medido:

Leer clientes Registrar cliente
Código viejo + schema nuevo ✅ ❌
Código nuevo + schema viejo ❌ ❌

Migrar primero deja el sistema funcionando: se vende, se consulta, se cobra, y lo único que falla es dar de alta un cliente nuevo durante los dos minutos del deploy. Al revés se cae todo lo de clientes, y con eso ventas, retornables y cartera.

Ventana de terminal
cd ~/code/aquazaku/api
DATABASE_MIGRATION_URL='<cadena de Supabase>' pnpm db:respaldo

C.2 — Migrar producción, ANTES de mergear

Sección titulada «C.2 — Migrar producción, ANTES de mergear»

La rama tiene que estar en tu máquina —ahí vive el archivo de la migración—.

Ventana de terminal
cd ~/code/aquazaku/api
git checkout <la-rama-de-la-PR>
Ventana de terminal
DATABASE_MIGRATION_URL='<cadena de Supabase>' pnpm db:migrate
Ventana de terminal
gh pr merge --merge

api primero y web después: el front llama al backend, no al revés.

Ventana de terminal
curl -s https://api.aquazaku.com/health

Es la parte que más se traba, y tiene dos trampas.

En el panel de Supabase: Project Settings → Database → Connection string.

Para migrar hacen falta estas características:

Valor
Modo sesión (no transacción)
Puerto 5432
Usuario postgres.<ref-del-proyecto>

El modo sesión es obligatorio porque las migraciones hacen DDL y REVOKE, y el modo transacción no los sostiene.

Lo que el migrador imprime, y por qué hay que leerlo

Sección titulada «Lo que el migrador imprime, y por qué hay que leerlo»

Antes de tocar nada, el script anuncia a dónde va:

→ migrando postgres.taytdxptguvrszjtsroz@aws-0-us-east-1.pooler.supabase.com/postgres → schema "public"
✓ migraciones aplicadas

La contraseña nunca aparece — está sacada a propósito, porque esta línea termina en logs y en capturas de pantalla.

Ventana de terminal
curl -s https://api.aquazaku.com/health

Bien:

{"status":"ok","service":"aquazaku-api","base":"ok","desdeHaceMs":19608}

Mal — faltan migraciones:

{"status":"ok","service":"aquazaku-api","base":"ok","desdeHaceMs":97950,
"esquema":"pendientes","faltan":["0016_nombre_partido"]}

Mientras aparezca faltan, el código desplegado está pidiéndole a la base algo que no tiene. status sigue diciendo ok porque el servidor está sano — lo que está mal es el schema, y por eso se reporta aparte.

Y la comprobación que ninguna consola reemplaza: entrá a app.aquazaku.com y registrá un cliente de verdad.

Un push a una PR despliega dos cosas más:

Plataforma Qué
Vercel La URL xxx-git-<rama>-aquazaku.vercel.app
Railway (staging) La api del preview, contra el schema preview

El startCommand de staging sí migra, porque va contra un schema desechable:

Ventana de terminal
pnpm db:sync-preview && pnpm db:seed && pnpm start

Cómo entrar al preview y qué usuario usar está en Entornos.

Síntoma Qué pasó Qué hacer
/health dice faltan: [...] Se mergeó sin migrar Correr la migración
ENOTFOUND db.<ref>.supabase.co Es la conexión directa, IPv6 Usar la del pooler
El migrador dice localhost La cadena no es la de producción Ctrl+C
500 en todas las pantallas de clientes Casi siempre el schema atrasado Mirar /health
El preview no acepta ningún usuario La api de staging no arrancó Log del deploy en Railway
CI en rojo pero GitHub deja mergear main no tiene protección de rama Decidirlo aparte

Código: revert del commit. Railway y Vercel redespliegan solos.

Migración: revertir el código no la deshace. Si la migración ya corrió, hay que escribir otra que la deshaga explícitamente y correrla igual que la primera. Por eso el respaldo del paso C.1 no es ceremonia.