Ir al contenido

Buscar en la documentación

Busca en la documentación de Skiffly

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:

package.json
{
  "scripts": {
    "build": "prisma generate && next build",
    "start": "next start",
    "postinstall": "prisma generate"
  }
}
prisma/schema.prisma
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:

app/healthz/route.ts
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_URL

Resultado 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 log

Railpack 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=production

La 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
\dt

skiffly 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 entorno
.skiffly/skiffly.ts
import { 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íntomaSolución
El build falla con @prisma/client did not initialize yetfalta prisma generate en postinstall/build
P1001: Can't reach database server at postgres:5432la 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 navegadorconfigura la variable y luego haz un build nuevo (skiffly up)

Siguiente: Guía de Next.js · Variables · Despliegues