How to host a Next.js app

To host a Next.js app, run next build and then next start on a server with the current Active LTS of Node.js. If the site needs no server features, generate a static export and serve the out folder.

Updated 6 min read

Learning objectives

After reading this article you will be able to:

  • Choose between a Node.js server, standalone output and a static export
  • Build and start the project with the right port and environment variables
  • Verify the deploy and fix the most common errors
On this page

Running npm run dev on your machine is only the start. For other people to reach the site, Next.js has to be built for production and kept on a server that stays on, with the right port, variables and Node version.

Next.js does not depend on any specific platform. The official documentation describes three ways to host it: a Node.js server, a Docker image, or static HTML files. Which one fits depends on the server features your project uses.

next buildNode.js serverthe .next foldernext startneeds Node.jsStandalone.next/standalonenode server.jsno node_modulesStatic exportthe out folderany static serverno Node.jsyour domain over HTTPS
One next build, three shapes: two need Node.js in production; the out folder only needs a static server.

Which hosting mode should you choose?#

Mode Config and command Needs Node.js in production? When to use
Node.js server next build + next start Yes Full app, every feature
Standalone output: 'standalone' + node server.js Yes, without node_modules Smaller deploy, Docker, containers
Static export output: 'export' No Site with no server features

Pick the static export only if the site works with no code running on the server. The documentation lists what is not supported in that mode: Server Actions, cookies, rewrites, redirects and headers in next.config, proxy (formerly middleware), ISR, Draft Mode, dynamic routes without generateStaticParams() and the default image optimizer. If you use any of them, stay with the Node.js server. For the static format in general, see what a static site is.

What do you need before you start?#

  • A recent Node.js version. The Next.js documentation lists the minimum it requires. In production, use the current Active LTS of Node.js; the official release schedule shows which one that is.
  • Scripts in package.json, which create-next-app already generates:
json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}
  • A committed lockfile (package-lock.json, pnpm-lock.yaml) so every machine installs the same versions.
  • A place to run it: a VPS, a container or a hosting platform. The difference is covered in VPS vs cloud platform.

How do you host it as a Node.js server?#

This is the path that supports every feature. On the server (or in CI), run:

bash
npm ci
npm run build
npm run start

next build creates the .next folder and prints a route table, where ○ is static and ƒ is rendered on demand. next start runs the production server on 0.0.0.0, port 3000 by default. To change the port, use the PORT variable or the -p flag:

bash
PORT=8080 npm run start
npx next start -p 8080

PORT must be in the process environment. Putting it in .env does not work, because the HTTP server starts before Next.js loads that file.

With next start, image optimization through next/image works with zero configuration. On glibc-based Linux, the documentation warns that it may need a memory allocator tweak to avoid excessive RAM use.

How do you build standalone or a static export?#

Standalone, for a smaller deploy#

Standalone mode copies only the files the server actually uses, including the needed parts of node_modules. Enable it in next.config.ts:

ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;

After next build, the .next/standalone folder contains a minimal server.js. It does not copy public or .next/static by default. Copy them yourself:

bash
npm run build
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
node .next/standalone/server.js

server.js reads PORT and HOSTNAME:

bash
PORT=8080 HOSTNAME=0.0.0.0 node .next/standalone/server.js

In monorepos, file tracing starts at the project folder. If files outside it are missing, set outputFileTracingRoot in next.config. To package everything in an image, see how to put an app in Docker.

Static export, for sites with no server-side code#

If the site is only content, change the config:

ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "export",
};

export default nextConfig;

next build now creates the out folder with one HTML file per route, plus CSS and JS. Any static file server can serve it. If you use next/image, define a custom loader in images, since the default optimizer needs a server.

How do you set environment variables?#

By default, variables are available only on the server. Those starting with NEXT_PUBLIC_ are inlined into the JavaScript during next build: changing the value later means building again. Variables read on the server during dynamic rendering (for example, after await connection()) are evaluated at run time, so one build can serve several environments.

Never put secrets in NEXT_PUBLIC_ variables, because they ship to the browser. The general pattern is in what environment variables are.

How do you check that it worked?#

  1. Logs: next start prints the address and port, with no errors after it.
  2. Request: open the home page and a dynamic route on the public address.
  3. Assets: in the browser, confirm the files under /_next/static/ load with status 200.
  4. Restart: restart the process and check that the site comes back on its own. The Next.js server finishes in-flight requests when it receives SIGTERM or SIGINT.

If a reverse proxy such as nginx sits in front, disable buffering so streaming works (for example, with the X-Accel-Buffering: no header).

What are the most common errors?#

  • Site opens locally but not on the public address: check the host and port the process listens on. next start already listens on 0.0.0.0 by default, but the standalone server.js binds to the address in the HOSTNAME variable. Set HOSTNAME=0.0.0.0 and PORT in the start command.
  • Page without CSS or broken images in standalone: you forgot to copy public and .next/static.
  • Failed to find Server Action: with several instances, each build generates its own encryption key. Set the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEY for all of them at build time.
  • A NEXT_PUBLIC_ variable with an old value: it was inlined in the build. Rebuild.
  • Process killed during next build: usually out of memory. Give it more RAM or build in CI instead.
  • Stale content with more than one instance: the page cache lives on each instance's disk. To share it, configure a cache handler, as the documentation describes.

How to do this on Square Cloud#

On Square Cloud, deploy is self-service: you upload the project and control the configuration. Node.js is one of several runtimes, and the static sites runtime is an option for the export's out folder: on it, nginx is always online serving the files, with none of your code running on the server. You can deploy by ZIP upload in the dashboard, the CLI, GitHub (a push deploys) or the VS Code extension.

The platform has no build step. Run next build in an external CI such as GitHub Actions and upload the result, or put the build in the start command: npm run build && npm start. In that case, everything the build needs must be in dependencies, because devDependencies are not installed. The server must listen on 0.0.0.0, on the port in the PORT variable, which next start already reads.

The squarecloud.app file (name, main file, memory and version) is optional: the dashboard generates it on deploy, or you can write it at the project root. Environment variables are set per application, in the dashboard, API, CLI or VS Code. The dashboard imports a .env, and a change applies on the next restart, except for NEXT_PUBLIC_ variables, which only change with a new next build.

The application runs in an always-on container. Automatic restart comes on when you deploy from the dashboard and only kicks in when the process exits with an error. The plan's RAM is a pool shared across your applications, with a 512 MB minimum per web application.

Every web application gets a *.squareweb.app subdomain with HTTPS, and a custom domain is available from the Standard plan (see how to use a custom domain). Plans are paid, start at R$12.49 per month and all include cookie-free web analytics. The details are on the sites page.

Website hosting

Static and SSR sites with automatic TLS and a global CDN

Learn more

FAQs

Which Node.js version does Next.js require?

The installation page of the Next.js documentation lists the minimum Node.js version. In production, use the current Active LTS of Node.js and the same version on your computer and on the server.

Can I host Next.js as a static site?

Yes. With output: 'export', next build generates an out folder with HTML, CSS and JS. Server Actions, cookies, redirects and rewrites in next.config, proxy (formerly middleware) and ISR do not work in that mode.

Do I need to run next build on the server?

Not necessarily. You can build on your computer or in CI and ship the result. With output: 'standalone', the .next/standalone folder holds only the files needed, with no node_modules install.

How do I change the Next.js port in production?

Use the PORT variable or the -p flag of next start. The default is 3000. PORT does not work inside a .env file, because the HTTP server starts before .env is read.

Why does my Next.js app work locally and break on the server?

The usual causes are a different Node version, environment variables missing at build or run time, a server listening only on localhost, and running out of memory during next build.

Next articleHow to host a React appTo host a React app built with Vite, run the build and publish the dist folder on any static file server. Covers routes, environment variables and domains.

More in Websites

See all 20 articles