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

# Deploy Next.js to Bunny Storage

> Build a Next.js site and serve it from Bunny Storage through Bunny CDN, with the Bunny CLI or by hand.

Next.js can export your app as static HTML, CSS, and JavaScript into `out`.

| Setting | Value |
| - | - |
| Build command | `npm run build` |
| Output directory | `out` |

## Configure Next.js

Turn on static export in `next.config.ts`, and write each page as a folder with an `index.html`. On an older project, the same options go in `next.config.js` or `next.config.mjs`:

```ts next.config.ts theme={null}
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "export",
  trailingSlash: true,
  images: { unoptimized: true },
};

export default nextConfig;
```

Without `trailingSlash`, Next.js writes `/about` as `about.html`, and a request for `/about` returns a 404.

`images.unoptimized` lets `next/image` build without the image optimisation server, which a static export doesn't have, and serves every image at full size. To resize them at the edge, see [Resize images with Bunny Optimizer](#resize-images-with-bunny-optimizer). A static export also can't use other features that need a server, such as API routes. See the [Next.js static exports guide](https://nextjs.org/docs/app/guides/static-exports) for the full list. To run Next.js with a server, deploy it with [Magic Containers](/docs/magic-containers/guides/nextjs.md).

## Deploy your site

**Bunny CLI**

> [!WARNING]
>
> `bunny sites` is in [Public Preview](/docs/product-release-stages.md#stage-2-public-preview)
> and available only in the Bunny CLI. It's in active development, so commands,
> flags, and behaviour may change. Share feedback on
> [Discord](https://discord.gg/bunnynet). The storage zone and pull zone behind
> each site appear in the dashboard under **Storage** and **CDN**.

**1. Install the Bunny CLI and log in**

```bash theme={null}
curl -fsSL https://cli.bunny.net/install.sh | sh
bunny login
```

See [Installation](/docs/cli/installation.md) for npm and other options.

**2. Create a site**

From your project root, run:

```bash theme={null}
bunny sites create my-site
```

Replace `my-site` with a name for your site. The CLI creates a storage zone for your files and a pull zone to serve them, then links this directory to the new site. The site is served at a `b-cdn.net` address, and the CLI prints the URL.

**3. Build and deploy**

```bash theme={null}
bunny sites deploy --build
```

The CLI runs the build command, uploads the output directory from the table above, and publishes it. Every deploy is kept, so you can roll back with `bunny sites deployments publish --previous`.

**4. Deploy on every push (optional)**

```bash theme={null}
bunny sites ci init
```

This writes a GitHub Actions workflow to `.github/workflows/bunny-sites.yml` that deploys the site on every push to `main`. Add your API key to the repository as a `BUNNYNET_API_KEY` secret. The CLI offers to run `gh secret set BUNNYNET_API_KEY` for you.

**Manually**

**1. Build your site**

From your project root, run `npm run build`. The build writes your site to the output directory listed in the table above.

**2. Create a storage zone**

In the [bunny.net dashboard](https://dash.bunny.net), go to **Storage** and click **Add Storage Zone**. Give it a name, pick the main region closest to your audience, and click **Add Storage Zone**.

**3. Upload your files**

Open the storage zone's file manager and upload the contents of your output directory to the root of the zone. To script uploads, use the [Storage HTTP API](/docs/storage/http.md).

**4. Create a pull zone**

Go to **CDN** and click **Add Pull Zone**. Set **Origin Type** to **Storage Zone**, select the zone you created, and click **Add Pull Zone**.

Your site is live at `https://<pull-zone-name>.b-cdn.net`.

## Resize images with Bunny Optimizer

A custom loader points `next/image` at [Bunny Optimizer](/docs/optimizer/dynamic-images/overview.md), which resizes each image at the edge for every width in the `srcset`. The [example app](https://github.com/BunnyWay/examples/tree/main/cdn/optimizer-nextjs-static) has the full setup.

**1. Turn on Optimizer**

`bunny sites create` leaves query strings and Optimizer off, so open the site's pull zone under **CDN → Pull Zones**:

* Turn on [URL Query String](/docs/cdn/vary-cache.md#url-query-string) vary, so each `?width=` gets its own cache entry.
* Enable [Bunny Optimizer](/docs/optimizer/quickstart.md), then turn on the Dynamic Image API and automatic WebP under **Optimizer → Settings**. Your build has already minified CSS and JavaScript, so turn Optimizer's minification off.

**2. Add the loader**

The loader adds `?width=` and `?quality=` to each image URL. It needs `"use client"` because the App Router sends the loader to the browser.

```typescript lib/bunny-loader.ts theme={null}
"use client";

import type { ImageLoaderProps } from "next/image";

const cdn = process.env.NEXT_PUBLIC_CDN_URL;

export default function bunnyLoader({ src, width, quality }: ImageLoaderProps) {
  const url = new URL(src, cdn || "http://localhost");
  const params = url.searchParams;

  if (!params.has("width")) params.set("width", String(width));

  if (!params.has("quality")) params.set("quality", String(quality ?? 75));

  const absolute = cdn || /^https?:\/\//.test(src);
  return absolute ? url.href : url.pathname + url.search;
}
```

**3. Swap unoptimized for the loader**

```ts next.config.ts theme={null}
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "export",
  trailingSlash: true,
  images: {
    loader: "custom",
    loaderFile: "./lib/bunny-loader.ts",
  },
};

export default nextConfig;
```

Deploy again, and the `srcset` URLs on each page carry `?width=`.

Any parameters already on `src` stay, so [Dynamic Image API](/docs/optimizer/dynamic-images/overview.md) options such as `?aspect_ratio=1:1` combine with the responsive widths. Next.js 16 only accepts quality 75 unless you list other values in `images.qualities`.

## Add a custom domain

With the Bunny CLI, add the domain to your site:

```bash theme={null}
bunny sites domains add example.com
```

The CLI prints the `CNAME` record to create with your DNS provider. Once the record resolves, issue a free certificate with `bunny sites domains ssl example.com`.

For a manual deploy, add the hostname to your pull zone by following [Custom hostnames](/docs/cdn/custom-hostname.md).


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