Running a Next.js app locally is npm run dev (or pnpm dev) for the dev server, npm run build && npm run start for the production build. The right answer is the dev server for development (with HMR, with source maps, with the dev tools), the production build for staging (with the optimized bundle, with the production env vars). The mistake every team makes: the team runs next dev in production, the team’s app is 10x slower than it should be, the team’s bundle is 10x larger than it should be.
Table of contents
- The dev command — the right answer for local development
- The build command — the right answer for the production bundle
- The start command — the right answer for the production server
- The .env file — the right way to set env vars locally
- The port — the default and the override
- The one mistake that breaks the localhost
- The standalone output — the right answer for a Docker image
- How this fits the rest of the stack
- FAQ
The dev command — the right answer for local development
The right answer for local development is npm run dev (or pnpm dev). The dev server has HMR (hot module replacement, the team’s changes are visible without a full reload), the dev server has source maps (the team’s errors are easier to read), the dev server has the dev tools (React DevTools, the Network tab, the bundle analyzer).
The build command — the right answer for the production bundle
The right answer for the production bundle is npm run build. The build runs the Next.js compiler (Turbopack or Webpack), the build generates the optimized bundle, the build generates the static pages (if applicable), the build outputs to .next/.
The gotcha: the build is slow on the first run (the team has no cache), the build is fast on subsequent runs (the team has the cache). The right answer is to commit the .next/cache to a build cache (Vercel’s cache, GitHub Actions cache), the wrong answer is to do a clean build on every deploy.
The start command — the right answer for the production server
The right answer for the production server is npm run start (which runs next start). The production server uses the optimized bundle, the production server has no HMR, the production server has no source maps (in production), the production server has the production env vars.
The mistake: the team runs next dev in production. The dev server is 10x slower than the production server, the bundle is 10x larger, the source maps are public (the team’s source code is exposed). The right answer is next start for production, the right answer is next dev for local dev only.
The .env file — the right way to set env vars locally
The right answer for local env vars is the .env.local file (not committed, the team’s personal env vars) or the .env.development file (committed, the team’s shared dev env vars). The right answer is .env.local for secrets, the right answer is .env.development for the team’s shared dev config.
The right answer is to load the env vars at server start (Next.js loads them automatically from .env.local). The wrong answer is to commit the secrets to the repo (the team’s secrets are exposed).
The port — the default and the override
The default port is 3000. The right answer for a team that has a conflict is to set the PORT env var (PORT=3001 npm run dev). The wrong answer is to change the code in next.config.js — the team’s change is permanent, the right answer is the env var.
The one mistake that breaks the localhost
The mistake: the team sets the HOSTNAME to localhost in the env, the team tries to access the app from a different host (a phone on the same network, a CI service), the app is not reachable. The right answer is to bind to 0.0.0.0 (HOSTNAME=0.0.0.0 npm run dev) so the app is reachable from any host.
The standalone output — the right answer for a Docker image
The right answer for a Docker image is the standalone output. The team sets output: 'standalone' in next.config.js, the build generates a .next/standalone/ directory with the minimal Next.js runtime. The right answer is the standalone output for a small Docker image, the right answer is the default output for a full Node.js deploy.
How this fits the rest of the stack
The infrastructure question is a small piece of a larger pattern: the team’s runtime, storage, database, secret store, logs, and deployment platform are all parts of the same platform. The right answer is to model the full stack before the project ships, not after. The RunxBuild hosting calculator is the right place to do that exercise — pick the runtime, the memory tier, the storage, the secret store, and the egress, and the calculator shows what the deploy actually costs at the team’s actual usage.
Useful related references:
FAQ
How do I run a Next.js app locally?
npm run dev for the dev server, npm run build && npm run start for the production build.
What is the difference between next dev and next start?
next dev is the dev server (HMR, source maps, dev tools). next start is the production server (optimized bundle, no HMR, no source maps). The wrong answer is next dev in production.
What is the default port for Next.js?
- Override with PORT=3001 npm run dev.
How do I bind Next.js to all interfaces?
HOSTNAME=0.0.0.0 npm run dev. The right answer is 0.0.0.0 for a team that needs to access the app from a different host.
How do I set env vars in Next.js?
Create a .env.local file in the project root. Next.js loads it automatically. The right answer is .env.local for secrets, the wrong answer is to commit the secrets to the repo.
What is the standalone output in Next.js?
A build option (output: ‘standalone’ in next.config.js) that generates a minimal Next.js runtime. The right answer for a small Docker image, the right answer is the default output for a full Node.js deploy.
How do I see the bundle size?
next build prints the bundle size. The right answer for a deeper analysis is the @next/bundle-analyzer package.
How do I see the errors in development?
The dev server prints the errors in the terminal and the browser console. The right answer is the terminal for server errors, the browser console for client errors.