NODE_ENV=production para un agente IA: no dejes development en Fly
Resumen
Cómo no correr Express en modo debug ni instalar devDependencies en prod: NODE_ENV=production en el runner, no en el stage de build. Vercel/Railway lo setean; Docker tienes que poner el ENV. Un next build con NODE_ENV ya production rompe el compile. Fuentes 3 sep 2026.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
NODE_ENV=development en Fly deja logs verbosos, stack traces al cliente y a veces pnpm install con typescript dentro. Node documenta la diferencia: libraries (Express, Next) cambian comportamiento. No es un detalle de estilo. Fuentes oficiales consultadas el 3 de septiembre de 2026.
Cuidado: el build de Next a menudo quiere NODE_ENV unset o production según el tooling. En este repo Hermes exporta production y next build se pelea: el compile se hace con NODE_ENV= vacío. El runtime del agente sí quiere production.
La regla
Runtime prod: NODE_ENV=production.
Build de la imagen: no heredes production si tu bundler se rompe (como Next).
Preview: production o preview, nunca copiar .env de laptop (dockerignore).
No uses NODE_ENV para “es staging”. Usa APP_ENV=staging. NODE_ENV tiene semántica de librerías.
Docker / multi-stage
En el runner: ENV NODE_ENV=production antes de pnpm install --prod (multi-stage). En el stage build no lo pongas si compilas Next/TS con scripts que miran development.
Railway/Fly variables de servicio pisan el Dockerfile. Alinea. Un dashboard en development gana al ENV de la imagen.
Vercel
Production / Preview / Development son tres buckets. Vercel setea NODE_ENV=production en deploys de prod y preview (preview no es development). development es vercel dev. No pongas NODE_ENV=development en Preview “para ver logs”: abre stack traces públicos.
Tabla

| Fase | NODE_ENV | Notas |
|---|---|---|
pnpm build Next | unset / según docs | no heredes Hermes production |
| runner Docker | production | ENV en último stage |
| Vercel prod | production | automático |
| Vercel preview | production | no es “dev” |
| laptop | development | vercel dev / node |
Errores comunes

| Síntoma | Causa | Fix |
|---|---|---|
| Stack al usuario | development | production |
| Imagen con eslint | install sin --prod / NODE_ENV | runner --prod |
next build raro en CI | NODE_ENV=production exportado | unset para el compile |
| Preview “demasiado honesto” | logs debug | no uses development en Preview |
| Staging = development | mal uso de NODE_ENV | APP_ENV |
Relación con el resto
- Install flaco: multi-stage.
- Env buckets: preview.
- Secretos no van en NODE_ENV: secretos.
- Logs: runtime logs.
Checklist
- Runner
ENV NODE_ENV=production - Dashboard Fly/Railway coincide
- CI compile no pisa mal el valor
-
APP_ENVseparado si hay staging - Preview no es development
- Ensayo:
docker run img printenv NODE_ENV→ production
FAQ
¿production desactiva source maps? Depende del bundler. No es un logger. Configura logs aparte.
¿Test? NODE_ENV=test o development en Vitest. No corras tests con production si el framework salta asserts.
¿Workers? No hay Express. Igual no dejes DEBUG=* en prod.
Fly y el shell del agente
Si el proceso arranca con npx o un script dev, NODE_ENV no importa: estás en el comando equivocado. CMD ["node","dist/server.js"] (no-root). Un pnpm start que mapea a tsx watch es development aunque el ENV diga production.
Compose environment: NODE_ENV=production pisa al Dockerfile. Bien si es consciente. Un env_file: .env de laptop con NODE_ENV=development lo echa a perder: no montes ese file en prod.
El ensayo: printenv NODE_ENV en el proceso que atiende Telegram, no en el job de build.
Si Hermes (u otro agente) exporta NODE_ENV=production en tu shell, un next build local miente. Unset para compile, set para el contenedor. Dos líneas en el README del agente.
No uses NODE_ENV para feature flags (NODE_ENV=canary). Las librerías no saben qué es canary. APP_ENV + kill switch.
Railway variables “shared” entre envs: no dejes development colado en prod por un copy-paste del dashboard. Revisa el overlay de Production vs el shared group.
DEBUG=* o LOG_LEVEL=debug es independiente de NODE_ENV. Production + DEBUG=express:* sigue siendo un leak de request bodies. Apaga DEBUG en prod aunque NODE_ENV esté bien.
Un healthcheck que imprime process.env entero es peor que development. Lista blanca de keys.
Qué dice Node (hoy) y qué no
La guía oficial: Node en sí no cambia entre development y production. No hay un flag del runtime que “active prod”. Lo que cambia es el ecosistema npm: librerías leen NODE_ENV y defaultan a development. La misma página dice: corre siempre con NODE_ENV=production. Twelve-factor para el resto de config.
El antipatrón que documentan: mezclar optimizaciones y comportamiento con el nombre del entorno. Código tipo if (NODE_ENV === 'development'), if (NODE_ENV === 'production'), if (['production','staging'].includes(NODE_ENV)). Staging no es un valor de NODE_ENV que Express entienda. Por eso APP_ENV=staging y NODE_ENV=production en el runner.
Cuatro entornos clásicos en esa página: Development, Testing, Staging, Production. Uno solo de esos cuatro merece el string production en NODE_ENV. Testing usa test si el runner lo pide. Staging no se llama development “para ver logs”.
Vercel: tres buckets, no dos
Docs de Environment Variables (hoy): Production aplica al Production Branch / vercel --prod. Preview aplica al push que no es ese branch, o vercel sin --prod. Development es local / vercel env pull / vercel dev (el CLI ≥ 22.0.0 baja Preview vars; vercel dev mete las de Development en memoria, no hace falta el pull). Custom environments existen aparte.
Framework env vars las rellena Vercel según el framework. No las uses para fingir NODE_ENV=development en un deploy Preview. Preview no es el bucket Development. Un stack trace en una URL *.vercel.app es público.
El build de Next en CI de este repo: si el shell trae NODE_ENV=production (Hermes lo exporta), next build se pelea. Unset solo para el compile. El contenedor / Function ya en marcha sí lleva production. Dos líneas, dos momentos.
Railway/Fly: la variable del dashboard pisa el ENV del Dockerfile. Revisa el overlay de Production vs el grupo shared. Un copy-paste de laptop con NODE_ENV=development gana.
Siguiente paso: si NODE_ENV ya es production y igual hay stack traces, logs y no imprimas err.stack al chat. Sin runtime: curso.
Lecturas relacionadas
Sigue explorando Deploy y otras piezas para builders.



