Ir al contenido

Buscar en la documentación

Busca en la documentación de Skiffly

Next.js

Despliega una app Next.js en Skiffly con Railpack — App Router o Pages, `next start` o salida standalone, `NEXT_PUBLIC_*` en tiempo de build, Postgres con Prisma o Drizzle, optimización de imágenes y notas sobre caché.

Para Railpack, un repositorio Next.js es un proyecto de Node: instala con el gestor de paquetes que implica tu lockfile, ejecuta npm run build (next build) y arranca con npm start (next start). No hay que configurar nada para un create-next-app recién creado. La página de Node.js cubre versiones, gestores de paquetes y el comportamiento general de Node; esta trata de lo que es específico de Next.js.

Puerto#

next start lee PORT del entorno, así que la app escucha donde Skiffly espera (el puerto del servicio; 8080 si no está definido). No pongas -p 3000 fijo en el script start a menos que también configures el puerto del servicio en 3000. next dev no es un servidor de producción; mantén start: next start.

Modos de renderizado#

next.config.*Qué hace RailpackNotas
por defecto (servidor)imagen de Node, next startSSR, route handlers, server actions e ISR funcionan; la caché de ISR vive en el disco del contenedor y es por réplica
output: "standalone"el mismo build; configura el comando de inicio en node .next/standalone/server.jsimagen más pequeña; copia public y .next/static tú mismo en el comando de build, p. ej. next build && cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/
output: "export"se detecta como sitio estático: el directorio exportado out/ lo sirve Caddysin funciones de servidor; consulta Sitios estáticos

Con la salida standalone, el servidor también lee PORT y se enlaza a 0.0.0.0 por defecto (define HOSTNAME=0.0.0.0 en versiones antiguas).

Variables de entorno en tiempo de build#

Los valores NEXT_PUBLIC_* se incrustan durante next build. Skiffly pasa todas las variables del servicio al build de Railpack, así que defínelas como variables normales del servicio antes de desplegar; cambiar una más tarde requiere un nuevo build (skiffly up, Desplegar en el panel, MCP deploy), no un reinicio. Los secretos exclusivos del servidor (DATABASE_URL, AUTH_SECRET) se leen en tiempo de ejecución y no necesitan un nuevo build.

Variables útiles en tiempo de ejecución:

NEXT_TELEMETRY_DISABLED=1
AUTH_URL=https://${{self.SKIFFLY_PUBLIC_DOMAIN}}      # URL canónica de NextAuth / Auth.js

Vincula la lista de hosts de confianza (AUTH_TRUST_HOST=true, serverActions.allowedOrigins) a tu dominio propio en cuanto agregues uno.

Base de datos#

skiffly deploy --template postgres
skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}'
  • Prisma: mantén prisma generate en postinstall (o "build": "prisma generate && next build"). Migra desde el comando de inicio: sh -c "npx prisma migrate deploy && next start", o como comando puntual con skiffly ssh -- npx prisma migrate deploy. No hay fase de release.
  • Drizzle: drizzle-kit migrate en el mismo lugar; drizzle-kit push está bien para prototipos.
  • Pool de conexiones: una réplica de Next.js abre un puñado de conexiones; con varias réplicas y route handlers al estilo serverless, un pooler (plantilla pgbouncer) mantiene tranquilo a Postgres.

La base de datos solo es alcanzable en la red privada; next build no debe consultarla (evita llamadas a la base de datos en el nivel superior de páginas generadas estáticamente, o márcalas con dynamic = "force-dynamic").

Imágenes, fuentes y caché#

  • La optimización de next/image corre en el proceso de Node; sharp lo instala el propio Next.js (npm i sharp en versiones antiguas). Permite los hosts remotos en images.remotePatterns.
  • next/font descarga las fuentes de Google en tiempo de build; el build tiene acceso a internet, así que funciona.
  • La caché de fetch y la de ISR se guardan en .next/cache, en el disco efímero del contenedor: al volver a desplegar arranca vacía, y las réplicas no la comparten. Para una caché compartida usa un cacheHandler personalizado respaldado por Redis (skiffly deploy --template redis).

Health check#

Configura la ruta del health check en / o en un route handler ligero (app/healthz/route.ts que devuelva Response.json({ ok: true })). Evita páginas que consulten la base de datos en cada sonda.

Monorepos (Turborepo, pnpm workspaces)#

Configura el directorio raíz del servicio en la app (apps/web) solo cuando la app sea autocontenida. Cuando importa paquetes del workspace, deja la raíz en el repositorio y define explícitamente los comandos de build y de inicio:

build: pnpm install --frozen-lockfile && pnpm --filter web build
start: pnpm --filter web start

Railpack detecta pnpm-workspace.yaml e instala desde la raíz; next.config con transpilePackages o outputFileTracingRoot se encarga del resto. El tutorial de monorepo recorre una configuración completa.

Problemas comunes#

SíntomaSolución
Despliegue FAILED, el log muestra Ready on http://localhost:3000 pero no llega tráfico-p 3000 fijo en start mientras el puerto del servicio es 8080: quita el flag o configura el puerto en 3000
NEXT_PUBLIC_API_URL es undefined en el navegadorla variable se agregó después del build; lanza un nuevo build
Error: Cannot find module 'sharp'agrega sharp a las dependencias
Build terminado a la fuerza (Killed, exit 137) durante next buildbaja experimental.cpus en next.config o define NODE_OPTIONS=--max-old-space-size=2048 como variable del servicio (llega al build)
Invalid src prop … hostname not configuredagrega el host a images.remotePatterns
Las server actions fallan detrás de un dominio propioAUTH_TRUST_HOST=true / experimental.serverActions.allowedOrigins

Ejemplo#

.skiffly/skiffly.ts
import { defineSkiffly, generate, github, postgres, project, service } from "@skiffly/config";
 
export default defineSkiffly(() => {
  const db = postgres("db");
  const web = service("web", {
    source: github("acme/shop"),
    start: "sh -c 'npx prisma migrate deploy && next start'",
    healthcheck: "/healthz",
    port: 3000,
    env: { NODE_ENV: "production", DATABASE_URL: db.env.DATABASE_URL, AUTH_SECRET: generate("hex32"), AUTH_URL: "https://${{self.SKIFFLY_PUBLIC_DOMAIN}}" },
    domain: true,
  });
  return project("shop", { resources: [db, web] });
});

Paso a paso: tutorial de Next.js + Postgres. Landing: /deploy/nextjs.