Ir al contenido

Buscar en la documentación

Busca en la documentación de Skiffly

Node.js

Cómo Railpack construye un servicio Node.js en Skiffly — detección, gestores de paquetes, la versión de Node, comandos de build y de inicio, `PORT`, bases de datos, migraciones, health checks y los fallos habituales.

Skiffly construye un repositorio con Railpack a menos que contenga un Dockerfile. Esta página describe qué hace el proveedor de Node de Railpack y las pocas opciones que quizá quieras tocar. Todo lo que aparece aquí aplica a Express, Fastify, NestJS, Hono, Remix, Nuxt, SvelteKit y cualquier otro framework que corra como un proceso de Node; Next.js tiene su propia página, y Bun también.

Qué detecta Railpack#

RevisaEfecto
package.json en el directorio raíz (o en el directorio raíz del servicio)el servicio es una app de Node
el campo packageManager, luego pnpm-lock.yaml, bun.lock/bun.lockb, .yarnrc.yml/yarn.lock, luego enginesqué gestor de paquetes instala las dependencias (npm por defecto)
la variable RAILPACK_NODE_VERSION, devEngines.runtime, engines.node, .nvmrc, .node-version, mise.toml/.tool-versionsla versión de Node (por defecto: la LTS actual)
workspaces en package.json, pnpm-workspace.yaml, nx.jsonmonorepo: instala desde la raíz y construye la app
scripts.buildel paso de build (npm run build), solo cuando el script existe
scripts.start, luego main, luego index.js/index.tsel comando de inicio

El resultado del build es una imagen OCI estándar; el log del build muestra el proveedor detectado, las versiones y los comandos como líneas [railpack] (skiffly logs --build).

Puerto#

Skiffly inyecta PORT (el puerto del servicio, 8080 cuando el servicio no define uno) y enruta hacia él el dominio público y el hostname privado. Escucha en ese puerto, en todas las interfaces:

const port = Number(process.env.PORT ?? 3000);
app.listen(port, "0.0.0.0");

Los binds a localhost/127.0.0.1 no son alcanzables desde el balanceador de carga y el despliegue termina como FAILED cuando el health check agota su tiempo de espera. Si tu app escucha en un puerto fijo (digamos 3000), configura el puerto del servicio en 3000 en lugar de reescribir la app: Configuración → Red → Puerto, skiffly deploy --port o port en configuración como código.

Comandos de build y de inicio#

Los valores por defecto sirven para la mayoría de las apps. Para sobrescribirlos:

OpciónDóndeVariable de Railpack detrás
Comando de buildConfiguración → Build → Comando de build, buildCommand, MCP update-serviceRAILPACK_BUILD_CMD
Comando de inicioConfiguración → Despliegue → Comando de inicio, startCommandRAILPACK_START_CMD
Comando de instalaciónuna variable del servicioRAILPACK_INSTALL_CMD

El comando de inicio reemplaza por completo lo que Railpack deduce: node dist/server.js, npm run start:prod, node --enable-source-maps build/index.js.

TypeScript: compila en el paso de build ("build": "tsc", inicio node dist/index.js) o ejecútalo directamente con un loader (node --import tsx src/index.ts); Railpack no agrega un compilador por ti.

Bases de datos y otros servicios#

Agrega Postgres o Redis desde una plantilla y referencia sus cadenas de conexión; nada se copia a mano:

skiffly deploy --template postgres
skiffly deploy --template redis
skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}' 'REDIS_URL=${{Redis.REDIS_URL}}'

Ambas URL apuntan al hostname privado (postgres:5432, redis:6379); no son alcanzables desde tu laptop, para eso usa skiffly connect postgres. Prisma, Drizzle, Knex, TypeORM, pg e ioredis leen estas variables tal cual. Prisma necesita generar su cliente durante el build: mantén prisma generate en postinstall o en el script de build.

Migraciones#

Skiffly no tiene fase de release. Dos formas que funcionan:

  • En el comando de inicio: se ejecuta en cada despliegue, antes de que la app escuche: sh -c "npx prisma migrate deploy && node dist/server.js". El arranque tiene cinco minutos antes de que el health check se rinda, suficiente para migraciones normales. Con varias réplicas, cada una ejecuta el comando; haz que las migraciones sean idempotentes (Prisma, Knex y Drizzle ya usan bloqueos).
  • Como comando puntual desde tu máquina, después del despliegue: skiffly ssh -- npx prisma migrate deploy. Combínalo con skiffly up -d en CI cuando quieras separar las migraciones de los arranques.

Health check#

Agrega una ruta que responda 200 rápidamente y configúrala como ruta del health check (/healthz). Los rollouts entonces cambian el tráfico solo cuando el contenedor nuevo responde, que es lo que hace que los despliegues sean sin tiempo de inactividad. Sin una ruta, Skiffly solo comprueba que el puerto acepte conexiones.

app.get("/healthz", (_req, res) => res.send("ok"));

Memoria y workers#

Una réplica recibe 2 vCPU / 2 GiB por defecto (Configuración → Recursos). Node no dimensiona su heap a partir del límite del cgroup por sí solo; para apps que consumen mucha memoria define NODE_OPTIONS=--max-old-space-size=1536 (alrededor del 75 % del límite) para que V8 recolecte antes de que el contenedor muera por OOM. Los workers en segundo plano son servicios separados del mismo repositorio, con su propio comando de inicio (node dist/worker.js) y sin dominio.

Problemas comunes#

SíntomaCausa / solución
FAILED después de 5 minutos, el log termina con Listening on http://localhost:3000no está enlazado a 0.0.0.0, o escucha en un puerto distinto de PORT: corrige el bind o configura el puerto del servicio para que coincida
npm ERR! missing script: startno hay script start ni main: agrega uno o define un comando de inicio
ERR_PNPM_OUTDATED_LOCKFILE / desajuste del lock en npm cilockfile desactualizado; ejecuta la instalación en local y haz commit del lockfile
El build funciona en local, falla con Cannot find moduleel módulo está en devDependencies y el build corre con RAILPACK_PRUNE_DEPS, o una ruta sensible a mayúsculas (./Utils vs ./utils)
Error: The engine "node" is incompatiblefija la versión con engines.node o RAILPACK_NODE_VERSION=22
OOMKilled en el log del sistemasube el límite de memoria o --max-old-space-size; busca fugas con skiffly logs --system
Prisma: @prisma/client did not initialize yetprisma generate no se ejecutó en el build; agrégalo a postinstall

Servicio de ejemplo#

.skiffly/skiffly.ts
import { defineSkiffly, github, postgres, project, redis, service } from "@skiffly/config";
 
export default defineSkiffly(() => {
  const db = postgres("db");
  const cache = redis("cache");
  const api = service("api", {
    source: github("acme/api"),
    start: "sh -c 'npx prisma migrate deploy && node dist/server.js'",
    healthcheck: "/healthz",
    port: 3000,
    env: { NODE_ENV: "production", DATABASE_URL: db.env.DATABASE_URL, REDIS_URL: cache.env.REDIS_URL },
    domain: true,
  });
  return project("acme-api", { resources: [db, cache, api] });
});

Relacionado: Servicios · Variables · Next.js · Landing de Node.js