> ## Documentation Index
> Fetch the complete documentation index at: https://docs.springwinter.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js deployment gotchas

> Choose static export or a Web server, set monorepo paths, and avoid common Next.js build, routing, and environment variable errors.

A Next.js frontend can need a running server. Choose the deployment type from the features your app uses, not from the fact that it is frontend code.

> [!WARNING]
>
> Spring Winter does not currently provide automatic Next.js framework builds or managed OpenNext/Lambda deployments. Static website serves files through S3 and CloudFront. Web server runs your container on Fargate.

## Choose a deployment type

| Your app needs | Choose | What you supply |
| - | - | - |
| Pages and assets that can all be generated during the build | Static website | A successful static export and its output folder |
| Request-time rendering, server-side authentication, Server Actions, or runtime API routes | Web server | A Dockerfile in GitHub or a public container image |

Vercel manages framework builds and server execution for you. A successful Vercel deployment does not mean the same app can be uploaded unchanged to a static host.

## Static website: no Dockerfile

Use this path only if your app supports static export. In `next.config.mjs`, merge this setting into your existing configuration:

```js theme={null}
const nextConfig = {
  output: 'export',
};

export default nextConfig;
```

Do not replace other application settings without reviewing them. Some features need changes: runtime redirects, rewrites, and response headers are not implemented by the exported files; image optimization needs an export-compatible loader or unoptimized images. Request-time features require a server. Check the [Next.js static export requirements](https://nextjs.org/docs/15/app/guides/static-exports) for your Next.js version.

Run the build locally first. Confirm that `out/index.html` exists. The normal `.next` build directory is not a static publish directory. Neither `dist` nor `public` is the default Next.js export output.

### App in a subfolder

For this example layout:

```text theme={null}
repository/
  server/
  frontend/
    package.json
    package-lock.json
    next.config.mjs
```

Use these Static website settings:

| Setting | Value |
| - | - |
| Build command | `cd frontend && npm ci && npm run build` |
| Output directory | `frontend/out` |

The build command starts at the repository root. The output directory also stays relative to the repository root, even after `cd` in the build command. Replace `frontend` with your folder name. For example, `surely-placed-fe` uses `cd surely-placed-fe && npm ci && npm run build` and `surely-placed-fe/out`.

If the app is at the repository root, use `npm ci && npm run build` and `out`. These examples assume the default export output and an npm lockfile in the app folder. Workspaces may need installation from the workspace root instead.

### A successful export does not prove nested routes work

Next.js can export a route such as `/about` to `out/about.html`, or to `out/about/index.html` with `trailingSlash` enabled. Spring Winter's current Static website flow does not create Next.js-specific URL-to-file routing rules. CloudFront's root `index.html` setting does not automatically resolve every nested directory.

Check direct visits and refreshes on nested routes after deployment. Do not turn on **SPA rewrite** as a general fix: it returns the root `index.html` for missing paths, rather than selecting each exported Next.js page. If your app needs clean Next.js routes and has no suitable routing configuration, use a Web server.

### Static websites have no application process

Do not configure `npm start`, a container port, a health path, or `PORT` for a Static website. There is no Next.js server running after the build. Keep your separate backend deployed separately and configure the frontend to call its public HTTPS URL.

## Web server: preserve server features

Use [Web servers](https://docs.springwinter.dev/deploy/web-servers.md) when the app needs Next.js at request time. Spring Winter currently builds a container from your committed Dockerfile or pulls an existing public image. It does not generate the Dockerfile automatically.

For an app and Dockerfile inside `frontend/`, use:

| Setting | Value |
| - | - |
| Root directory | `frontend` |
| Dockerfile path | `frontend/Dockerfile` |
| Container port | `3000` |
| Health path | `/`, only if it returns HTTP 200 without login or redirects |

The root directory selects the Docker build context. The Dockerfile path stays relative to the repository root. Inside this Dockerfile, `COPY . .` copies from `frontend/`.

Build the application during image creation. For a standard `next start` deployment, bind to all interfaces:

```sh theme={null}
npm run start -- --hostname 0.0.0.0 --port 3000
```

This assumes your `start` script runs `next start`. A standalone-output image instead runs its generated `server.js`; follow the [Next.js self-hosting guidance](https://nextjs.org/docs/15/app/guides/self-hosting) for the packaging mode you choose. Keep the process port and Spring Winter container port consistent. If `/` redirects or requires login, add a dedicated endpoint that returns HTTP 200 without authentication and set that as the health path.

## Environment variable gotchas

* Variables starting with `NEXT_PUBLIC_` are embedded in browser JavaScript during the Next.js build. They are public. Never put private credentials in them.
* On a Static website, add required values in **Environment** before the build, then redeploy after changes.
* On a Web server, **Environment** configures the running container. The current Docker build does not automatically receive these customer variables as build arguments. Changing a runtime value cannot replace a value already embedded in browser JavaScript.
* For a Web server that needs public build-time values, build a public image through your own build pipeline with those values supplied, or explicitly configure non-secret public values in your Docker build. Do not commit secret `.env` files or copy them into an image.
* Runtime secrets belong in the running service environment. Static exports have no private server runtime. Data included in exported pages is public.

See [Next.js environment variables](https://nextjs.org/docs/15/app/guides/environment-variables) and [Spring Winter environment variables](https://docs.springwinter.dev/concepts/environment-variables.md).

## Common build errors

| Error or symptom | What to do |
| - | - |
| `npm ci` requires a lockfile | Run `npm install` locally in the correct app or workspace directory, review and commit `package-lock.json`, then redeploy. If dependencies have not been locked yet, `npm install && npm run build` can get past this error, but dependency resolution can change between builds. |
| `Build output directory not found` | Read the first build error. Fix it before changing the output path. For a successful default Next.js static export, use `out`, prefixed with the app folder when applicable. |
| `failed to read dockerfile` | For a Web server, commit the Dockerfile and check its repository-relative path. A Static website does not use a Dockerfile. |
| Build succeeds but a nested URL returns 403/404 | Check the exported filename and CDN routing. Test direct navigation and refresh, not only links from the home page. |
| Web server builds but stays unhealthy | Check application logs, binding to `0.0.0.0`, the configured port, and whether the health path returns HTTP 200. |
| New public environment value does not appear | Rebuild with the value available during `next build`; restarting the same image is insufficient. |

## Before you deploy

Confirm the deployment type, package manager, lockfile, app directory, and required build-time values. For static export, verify the output folder and direct nested URLs. For a Web server, verify the Docker build and health endpoint. Test login, API calls, images, redirects, and refreshes on the deployed URL.

OpenNext is not a setting you can enable in the current Static website flow. Spring Winter does not automatically move Next.js routes or a separate backend to Lambda.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.