Migraciones de base de datos en el deploy

Ejecuta las migraciones como paso explícito antes de arrancar el código nuevo, con el comando de deploy de tu herramienta: prisma migrate deploy, drizzle-kit migrate o alembic upgrade head. Haz un snapshot antes.

Actualizado el 7 min de lectura

Objetivos de aprendizaje

Después de leer este artículo podrás:

  • Ejecutar migraciones de Prisma, Drizzle o Alembic como paso del deploy
  • Escribir cambios de esquema que no bloqueen una tabla con mucho tráfico
  • Comprobar que una migración se aplicó y recuperarte cuando falla
En esta página

Una migración es un archivo versionado que cambia el esquema de tu base de datos: crear una tabla, añadir una columna, crear un índice. La herramienta registra qué archivos ya se ejecutaron, así que todos los entornos terminan con el mismo esquema. Lo difícil no es escribir el archivo. Es ejecutarlo en el momento correcto del deploy, una sola vez, con un camino de vuelta.

Esta guía cubre las tres configuraciones más comunes (Prisma, Drizzle y Alembic), el orden de los pasos y cómo escribir cambios que no congelen una tabla con mucho tráfico. Si tu versión de la herramienta es distinta, revisa los comandos en su documentación oficial.

npm run migrate && npm startSnapshotpunto derestauración1Migraciónuna vez,en un lugar2Comprobar¿salida 0?3Arrancarcódigo nuevo4fallóDeploy detenidoel código nuevo no arrancarestaurar
Snapshot, migración, comprobación y solo entonces el código nuevo; con &&, una migración fallida detiene el deploy.

¿Qué necesitas antes de empezar?#

¿Cuál es el orden correcto de un deploy con migraciones?#

Cuatro pasos, en este orden:

  1. Guarda un punto de restauración. Un dump o un snapshot de la base de datos hecho por la plataforma.
  2. Aplica las migraciones. Un proceso, una vez, con el comando de deploy de la herramienta.
  3. Comprueba el resultado. El código de salida del comando debe ser 0, y la herramienta debe indicar que el esquema está al día.
  4. Arranca el código nuevo. Solo después de que el paso 3 haya salido bien.

El paso 4 depende del paso 3. Si la migración falla y la aplicación arranca igual, el código consulta columnas que no existen. Encadenar los comandos con && garantiza ese orden sin esfuerzo, porque el segundo comando no se ejecuta cuando el primero falla.

Ten cuidado con varias instancias de la aplicación. Si dos arrancan a la vez y ambas ejecutan la migración, pueden chocar. Ejecuta la migración desde un único lugar (un job de CI o una instancia), no desde cada réplica.

¿Cómo se ejecutan migraciones con Prisma?#

En producción se usa prisma migrate deploy, que aplica los archivos de migración que ya están en tu repositorio. La documentación de Prisma indica ejecutarlo como parte del pipeline de CI/CD y desaconseja lanzarlo desde tu máquina contra una base de datos de producción. En cambio, prisma migrate dev es un comando de desarrollo, y la documentación dice que nunca debe usarse en producción. La CLI prisma suele ser una dependencia de desarrollo, así que debe estar instalada donde se ejecuta el comando.

json
{
  "scripts": {
    "migrate": "prisma migrate deploy",
    "start": "npm run migrate && node dist/index.js"
  }
}

En local creas la migración con npx prisma migrate dev --name add_orders_table, haces commit de la carpeta generada en prisma/migrations y dejas que el deploy la aplique.

¿Cómo se ejecutan migraciones con Drizzle?#

Drizzle divide el trabajo en dos. drizzle-kit generate escribe archivos SQL a partir de tu esquema, y drizzle-kit migrate aplica los que aún no se aplicaron. Necesita el dialect y las credenciales de conexión, en drizzle.config.ts o con las flags --dialect y --url. Las migraciones aplicadas quedan registradas en una tabla __drizzle_migrations, en el esquema drizzle por defecto.

json
{
  "scripts": {
    "generate": "drizzle-kit generate",
    "migrate": "drizzle-kit migrate",
    "start": "npm run migrate && node dist/index.js"
  }
}

drizzle-kit suele ser una dependencia de desarrollo, así que debe estar instalado donde se ejecuta el comando. Para no depender de él, el paquete drizzle-orm incluye un migrator que puedes llamar desde el código antes de que arranque el servidor:

ts
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
await migrate(drizzle(pool), { migrationsFolder: "./drizzle" });
await pool.end();

¿Cómo se ejecutan migraciones con Alembic?#

Alembic es la herramienta de migraciones de SQLAlchemy en Python. Creas el entorno una vez con alembic init alembic, creas una revisión con alembic revision -m "create account table" y aplicas todo con alembic upgrade head.

bash
alembic upgrade head && python main.py

Otros dos comandos ayudan en un deploy. alembic current muestra la revisión en la que está la base de datos, y head solo aparece cuando coincide con la última revisión de tu código. alembic downgrade -1 retrocede una revisión. Deja la URL de la base de datos fuera de alembic.ini: lee DATABASE_URL del entorno en env.py para que ninguna contraseña acabe en Git.

¿Cómo se escribe una migración que no bloquee la tabla?#

La mayoría de los cambios de esquema toman un bloqueo sobre la tabla, y las consultas de tu aplicación esperan mientras se mantiene. En PostgreSQL, ALTER TABLE toma un bloqueo ACCESS EXCLUSIVE, que impide lecturas y escrituras, salvo en las formas en que la documentación indica otro bloqueo. En una tabla pequeña dura milisegundos. En una grande, un recorrido dentro del bloqueo puede congelar la aplicación. Algunos hábitos ayudan:

  • Añade columnas con un default constante. Para un DEFAULT no volátil, PostgreSQL guarda el valor en los metadatos de la tabla en lugar de reescribir cada fila, así que la sentencia es rápida incluso en tablas grandes.
  • Divide el NOT NULL. SET NOT NULL recorre toda la tabla. Añade una restricción CHECK (col IS NOT NULL) como NOT VALID, valídala aparte y después aplica NOT NULL: PostgreSQL se salta el recorrido cuando un check válido ya demuestra que no hay nulos.
  • Añade claves foráneas como NOT VALID. ADD FOREIGN KEY ... NOT VALID se salta el recorrido largo, y VALIDATE CONSTRAINT después solo toma un bloqueo SHARE UPDATE EXCLUSIVE.
  • No renombres ni elimines en el mismo deploy que el cambio de código. El código antiguo que sigue corriendo (o un rollback) se rompería. Añade la columna nueva, publica el código que la usa y elimina la antigua en un deploy posterior.
  • Saca las correcciones grandes de datos de la migración. Actualizar millones de filas en una sola sentencia mantiene bloqueos y llena el log. Haz el backfill por lotes desde un script.

¿Cómo sabes si la migración funcionó, y qué hacer si falló?#

Comprueba tres cosas después del comando: el código de salida, el estado de la propia herramienta (alembic current o prisma migrate status) y el esquema en sí (\d nombre_tabla en psql). Después lee los logs de la aplicación cuando arranque. Consulta cómo leer los logs de tu aplicación.

Cuando una migración falla, detén el deploy y lee el error. En PostgreSQL, el DDL corre dentro de una transacción, así que el cambio suele revertirse, y corriges el archivo y lo ejecutas de nuevo. MySQL confirma la mayoría de los cambios de esquema de forma implícita, así que un fallo puede dejar un cambio aplicado a medias que corriges a mano. En ambos casos, el punto de restauración del paso 1 es tu salida. Más sobre volver atrás en rollbacks y deploys sin downtime.

Errores comunes:

  • Editar una migración que ya se ejecutó en producción. Crea una nueva.
  • Ejecutar migrate dev o --autogenerate a ciegas en producción. Lee antes el SQL generado.
  • Dejar que cada réplica ejecute la migración al arrancar.
  • Poner la cadena de conexión en el repositorio en lugar de en una variable de entorno.

Cómo hacerlo en Square Cloud#

Square Cloud ofrece hosting de PostgreSQL y MySQL (además de MongoDB y Redis) a partir del plan Standard, en el mismo datacenter que tus aplicaciones. El panel te da la URL de conexión completa y un certificado para descargar; TLS es obligatorio, así que el driver debe confiar en la CA descargada (y, en PostgreSQL, también presentar el certificado .pem).

Guarda la URL de conexión como variable de entorno de la aplicación, desde el panel, la API, la CLI o VS Code. El cambio se aplica en el siguiente reinicio, sin rebuild. Después elige dónde se ejecuta la migración:

  • Al arrancar. El comando de inicio se ejecuta como comando de shell, así que npm run migrate && npm start funciona, igual que la llamada migrate() de Drizzle desde el código. No hay etapa de build, y las dependencias se instalan al arrancar con NODE_ENV=production, así que las devDependencies no se instalan: deja prisma o drizzle-kit en dependencies y genera dist/ en CI o dentro del comando de inicio.
  • Desde tu máquina o desde CI. La base de datos acepta conexiones desde fuera con su host, puerto, contraseña y certificado, así que un job de CI puede migrarla antes de que hagas el deploy con GitHub Actions.

Si varias aplicaciones comparten la base de datos, ejecuta la migración desde un solo lugar. Para el punto de restauración, haz un snapshot manual antes del deploy. Los snapshots manuales existen en todos los planes, y un snapshot de base de datos se puede restaurar en la misma base, en otra del mismo motor o en una base nueva, así que puedes probar antes una migración arriesgada en una copia. Todo plan que incluye bases de datos tiene también un snapshot automático diario, que se guarda 7 días.

Consulta la página de hosting de bases de datos para ver planes y motores.

Hosting de bases de datos

MongoDB, PostgreSQL, MySQL y Redis junto a tu app

Más información

Preguntas frecuentes

¿Las migraciones deben ejecutarse automáticamente en cada deploy?

Para una sola instancia de la aplicación, sí: así el código y el esquema van a la par. Ejecútala una vez por deploy, como paso propio, y haz que el deploy se detenga si la migración falla.

¿Qué pasa si una migración falla a medias?

Depende de la base de datos. PostgreSQL puede revertir DDL dentro de una transacción, así que una migración fallida no suele dejar rastro. MySQL confirma la mayoría de los cambios de esquema de forma implícita, así que un fallo puede dejar un cambio parcial que corriges a mano.

¿Es seguro ejecutar prisma migrate dev en producción?

No. Prisma documenta migrate dev como un comando de desarrollo que nunca debe usarse en producción. En producción se usa prisma migrate deploy, que solo aplica migraciones que ya existen.

¿Cómo deshago una migración?

Alembic puede ejecutar pasos de downgrade, pero la mayoría de los equipos prefiere una nueva migración hacia adelante que revierta el cambio. Restaurar un snapshot hecho antes del deploy es la red de seguridad para los datos.

Siguiente artículoMongoDB: collection, cluster y replica set explicadosUna collection agrupa documentos, un replica set es un grupo de servidores con copias de los mismos datos y un cluster es el conjunto que sirve la base.

Más en Bases de datos

Ver los 24 artículos