Desplegar Next.js con Postgres
Un tutorial paso a paso: una app Next.js con Prisma y una base de datos Postgres en Skiffly, desde un proyecto vacío hasta una URL pública con migraciones, usando el panel, la CLI o un agente MCP.
Qué vas a construir: un proyecto con dos servicios — web (Next.js 15, App Router, Prisma) y Postgres (desde la plantilla) — un dominio generado con TLS, migraciones que se ejecutan en cada despliegue y un entorno de vista previa por cada pull request. Unos 15 minutos.
Necesitas: una cuenta de GitHub, Node.js 20+ y una app Next.js que use Prisma (o parte de npx create-next-app@latest y npx prisma init). Inicia sesión una vez en app.skiffly.dev.
1. Preparar la app#
Dos cosas en el repositorio, ambas práctica habitual en Next.js:
{
"scripts": {
"build": "prisma generate && next build",
"start": "next start",
"postinstall": "prisma generate"
}
}datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}Agrega una ruta de salud para que los rollouts cambien el tráfico solo cuando el contenedor nuevo responda:
export function GET() {
return Response.json({ ok: true });
}Haz commit y push. next start lee PORT, así que no hace falta código para el puerto.
2. Crear el proyecto y la base de datos#
npm i -g skiffly && skiffly login
cd my-shop
skiffly init --name shop # proyecto "shop", directorio vinculado
skiffly deploy --template postgres # servicio "Postgres", volumen, DATABASE_URLResultado esperado: skiffly status muestra Postgres como SUCCESS en menos de un minuto.
3. Desplegar la app#
skiffly up # crea el servicio "my-shop", sube el directorio, compila con Railpack y transmite el logRailpack detecta package.json, instala con el gestor de paquetes de tu lockfile, ejecuta npm run build y arranca npm start. El primer build tarda 2–4 minutos; el log termina con Deployment SUCCESS.
Si quieres, renombra el servicio a web: skiffly api 'mutation { serviceUpdate(id: "<id>", input: { name: "web" }) { id } }' o desde el panel.
La app ya está construida, pero todavía no puede llegar a la base de datos: ese es el siguiente paso.
4. Conectar la base de datos y ejecutar las migraciones#
skiffly variables set -s web 'DATABASE_URL=${{Postgres.DATABASE_URL}}' NODE_ENV=productionLa referencia se resuelve al momento del despliegue a postgresql://postgres:…@postgres:5432/app en la red privada. Skiffly no tiene fase de release, así que haz que el comando de inicio ejecute las migraciones antes del servidor:
skiffly api 'mutation { serviceInstanceUpdate(serviceId: "<web id>", environmentId: "<env id>", input: { startCommand: "sh -c \"npx prisma migrate deploy && next start\"", healthcheckPath: "/healthz" }) }'
skiffly redeploy -s web(skiffly status --json imprime los ids.)
Resultado esperado en skiffly logs -s web -f: el All migrations have been successfully applied de Prisma y luego Ready in …. El despliegue pasa a SUCCESS cuando /healthz responde.
¿Prefieres tener las migraciones bajo tu control? Deja el comando de inicio como next start y ejecuta skiffly ssh -s web -- npx prisma migrate deploy después de cada despliegue que incluya una migración.
5. Obtener una URL#
skiffly domain -s web # https://web-x1y2.skiffly.cloudÁbrela. Para app.example.com: skiffly domain app.example.com, crea los registros CNAME y TXT que imprime y ejecuta skiffly domain status app.example.com hasta que quede verificado. Apunta AUTH_URL/NEXTAUTH_URL y similares a la URL final; https://${{self.SKIFFLY_PUBLIC_DOMAIN}} sigue el dominio generado automáticamente.
6. Mirar dentro de la base de datos#
skiffly connect postgres # abre psql a través de un proxy TCP temporal
\dtskiffly proxy delete <port> cierra el proxy público después.
7. Vistas previas por pull request#
Proyecto → Configuración → Vistas previas de PR (o projectUpdate(id, input: { prDeploys: true })). Cada PR recibe un entorno pr-<n>: una copia de las variables, web construido desde la rama del PR y su propio Postgres vacío (los datos no se copian; el prisma migrate deploy del comando de inicio levanta el esquema). El entorno se elimina cuando el PR se cierra.
8. Lo mismo como código#
Adopta lo que construiste en un archivo y guárdalo en el repositorio:
skiffly config init # escribe .skiffly/skiffly.ts a partir del entornoimport { defineSkiffly, github, postgres, project, service } from "@skiffly/config";
export default defineSkiffly(() => {
const db = postgres("Postgres");
const web = service("web", {
source: github("acme/my-shop"),
start: 'sh -c "npx prisma migrate deploy && next start"',
healthcheck: "/healthz",
port: 3000,
env: { NODE_ENV: "production", DATABASE_URL: db.env.DATABASE_URL },
domain: true,
});
return project("shop", { resources: [db, web] });
});skiffly config plan no muestra cambios; a partir de ahora skiffly config apply en CI reconcilia la configuración.
Solución de problemas#
| Síntoma | Solución |
|---|---|
El build falla con @prisma/client did not initialize yet | falta prisma generate en postinstall/build |
P1001: Can't reach database server at postgres:5432 | la referencia está en el servicio equivocado, o Postgres todavía está arrancando: skiffly status |
El despliegue se queda en DEPLOYING y luego pasa a FAILED | -p 3000 fijado en start mientras el puerto del servicio es 8080; quita el flag o configura el puerto 3000 |
NEXT_PUBLIC_* es undefined en el navegador | configura la variable y luego haz un build nuevo (skiffly up) |
Siguiente: Guía de Next.js · Variables · Despliegues