Guía9 min

NODE_ENV y Next.js para agentes: no dejes production pegado en el shell

Resumen

Next.js carga .env* y trata NODE_ENV como suyo: next build es production, next dev es development. Un harness que exporta NODE_ENV=production rompe tests y a veces el build. Esta guía cubre unset para next build, development para vitest, NEXT_PUBLIC_ y no commitear .env. Docs Next 16.

GitHub
Un shell con NODE_ENV production que un agente debe unset antes de next build

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.

Varios agentes (Hermes incluido) arrancan con NODE_ENV=production. En una app Next eso no es “más realista”: rompe el loop local. next build ya fuerza production por su cuenta. Si el padre del proceso ya trae production, Vitest, next dev y a veces el propio build se pelean.

La doc de Environment Variables (Next 16.3, actualizada 2026-08-25): Next carga .env* solo desde la raíz del proyecto, no desde /src. create-next-app mete .env* en gitignore: casi nunca quieres commitearlos. Eso encaja con gitignore vs sandbox.

Quién pone NODE_ENV

ComandoNODE_ENV que espera
next devdevelopment
next buildproduction (lo setea Next)
next startproduction
vitest / pnpm testtest o development, no production

Receta para el agente:

NODE_ENV=development pnpm test
NODE_ENV=development pnpm lint
unset NODE_ENV
pnpm build

unset es más seguro que NODE_ENV=production pnpm build heredado del padre: dejas que Next lo ponga. Si el harness no puede unset, env -u NODE_ENV pnpm build.

Shell padre en production vs comandos locales

.env vs NEXT_PUBLIC_

Next:

  • Variables en .envprocess.env en servidor (Route Handlers, etc.).
  • Prefijo NEXT_PUBLIC_ → se bundlea al browser. Un agente que pone el API key en NEXT_PUBLIC_STRIPE acaba en el JS del cliente.

Multiline y \n en .env están documentados. El agente no “arregla” una clave PEM partiéndola mal.

@next/env + loadEnvConfig(process.cwd()) si un ORM o Vitest corre fuera del runtime Next. No reimplementes el parser.

.env vive en la raíz, no en src/. Un worktree nuevo no hereda el .env del principal: cópialo a propósito o no.

Lo que el agente no hace

  • echo SECRET >> .env y git add .env.
  • NODE_ENV=production pnpm test “para ver prod”.
  • Inventar .env.local con keys del chat.
  • Asumir que Vercel inyecta las mismas vars en local.

El curso instalar un agente no cubre el env del proceso padre. Sandboxing no unsetea variables.

NEXT_PUBLIC en el bundle vs secretos de servidor

Checklist AGENTS.md

Test/lint: NODE_ENV=development pnpm …
Build: unset NODE_ENV && pnpm build
Never commit .env*
Never NEXT_PUBLIC_ for secrets
.env only at repo root

FAQ

¿Por qué production rompe Vitest? jsdom / next test utils asumen development. Production minifica y salta asserts.

¿.env.production en build? Next lo carga en build/start. No lo commitees si tiene secretos.

¿Turbopack? El env es el mismo. Un symlink de node_modules fuera del root es otro error.

Si el CI de Vercel está verde y el agente local no, lo primero es echo $NODE_ENV en el mismo shell. Nueve de diez veces el padre dejó production. Un printenv | grep NODE antes de test/build es más barato que un issue de “Vitest 0 tests”.

Verificado 2026-09-03 contra Next.js Environment Variables (16.3.4).