Skip to content
For the complete Netlify documentation index, see llms.txt. Markdown versions of any documentation page are available by appending .md to its URL.

Local development

When you run your project locally with the Netlify CLI, your server runs alongside your frontend, functions, and edge functions.

Locally, your server runs on the same platform code that runs it in production. The entry file is loaded the same way, and it gets the same context and lifecycle. Refer to Differences from production for what isn't the same.

Run the Netlify dev server from the root of your project, using version 27.17.0 or later of the Netlify CLI:

Terminal window
netlify dev

Your server runs on the version of Node.js installed on your machine. Use Node.js 24 or later, as in production.

The CLI starts your server in its own Node.js process the first time a request needs it, and stops it when the dev server stops.

The Netlify Vite plugin doesn't run your server yet. Use the Netlify CLI instead.

Locally, requests reach your server in the same order as in production:

  • Static files, functions, and edge functions take precedence over your server.
  • Your server receives every request that nothing else handles. Locally, this is the case even if it sets path or excludedPath.
  • WebSocket upgrades reach your server too, so you can test real-time features locally.

The CLI watches the netlify/server/ directory. When a file in it is added, changed, or removed, the CLI stops the running server and starts a new one with your changes on the next request.

Stopping the server works the same way as shutdown in production. Your process receives SIGTERM, and your shutdown hooks run before it exits. Locally, the whole shutdown, including waiting for requests in progress, has 5 seconds before the process is stopped.

Changes to files outside netlify/server/, such as modules your server imports from elsewhere in your project, don't restart it. Restart netlify dev to pick those up.

The same engine that powers netlify dev is available as the @netlify/dev package, which is useful for end-to-end tests that need your server running with the rest of your project:

Terminal window
npm install --save-dev @netlify/dev
import { NetlifyDev } from "@netlify/dev";
const netlifyDev = new NetlifyDev({
projectRoot: "./fixtures/my-project",
server: { enabled: true },
});
await netlifyDev.start();
// ...run your tests...
await netlifyDev.stop();

Your server runs on the same platform code locally as in production, with a few differences to keep in mind:

  • Locally there is always a single instance, so behavior that depends on several instances running at once, such as in-memory state not being shared between them, doesn't show up.
  • Your server's path and excludedPath aren't applied locally, so it receives every request that nothing else handles.
  • The limits that apply in production, such as how long a request can run, aren't enforced locally.
  • The working directory of your server isn't the same locally as in production. Resolve files relative to the module that reads them, for example with import.meta.dirname, rather than relative to process.cwd().
  • Shutdown has 5 seconds in total locally, compared to up to 115 seconds in production.
  • On Windows, the server process is stopped without receiving SIGTERM, so your shutdown logic doesn't run locally.
  • Health check hooks don't run locally, since there's only one instance and no routing between instances to inform.