> ## 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 Astro to Bunny Storage

> Build an Astro site and serve it from Bunny Storage through Bunny CDN, with the Bunny CLI or by hand.

export const build_0 = "npm run build"

Astro builds static HTML by default, and writes every page to `dist`.

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

## Deploy your site

<Tabs>
  <Tab title="Bunny CLI">
    <Warning>
      `bunny sites` is in [Public Preview](/docs/product-release-stages#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**.
    </Warning>

    <Steps>
      <Step title="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) for npm and other options.
      </Step>

      <Step title="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.
      </Step>

      <Step title="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`.
      </Step>

      <Step title="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.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Manually">
    <Steps>
      <Step title="Build your site">
        From your project root, run <code>{build_0}</code>. The build writes your site to the output directory listed in the table above.
      </Step>

      <Step title="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**.
      </Step>

      <Step title="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).
      </Step>

      <Step title="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`.
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Resize images with Bunny Optimizer

Astro's `<Image>` component can hand every width to [Bunny Optimizer](/docs/optimizer/dynamic-images/overview) through a custom image service. The build then skips image processing, and the edge resizes each width in the `srcset`.

<Steps>
  <Step title="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#url-query-string) vary, so each `?width=` gets its own cache entry.
    * Enable [Bunny Optimizer](/docs/optimizer/quickstart), 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.
  </Step>

  <Step title="Add the image service">
    The service spreads Astro's `baseService`, so Astro still works out the `srcset` widths, and only `getURL` changes. The [example app](https://github.com/BunnyWay/examples/tree/main/cdn/optimizer-astro-static) has the full setup.

    ```ts src/bunny-image-service.ts theme={null}
    import type { ExternalImageService } from "astro";
    import { baseService } from "astro/assets";

    const service: ExternalImageService<{ baseURL?: string }> = {
      ...baseService,
      getURL(options, imageConfig) {
        const base = imageConfig.service.config?.baseURL;
        const src = typeof options.src === "string" ? options.src : options.src.src;

        const absolute = /^(https?:)?\/\//.test(src);
        if (absolute && !(base && src.startsWith(base))) return src;

        const url = new URL(src, base || "http://localhost");
        if (options.width) url.searchParams.set("width", String(options.width));
        url.searchParams.set("quality", String(options.quality ?? 75));
        return base || absolute ? url.href : url.pathname + url.search;
      },
    };

    export default service;
    ```

    ```js astro.config.mjs theme={null}
    import { defineConfig } from "astro/config";

    export default defineConfig({
      image: { service: { entrypoint: "./src/bunny-image-service.ts" } },
    });
    ```
  </Step>

  <Step title="Add images">
    Import images from `src/assets`, or reference files in `public` by path:

    ```astro theme={null}
    ---
    import { Image } from "astro:assets";
    import photo from "../assets/photo.jpg";
    ---
    <Image src={photo} alt="Photo" layout="constrained" />
    <Image src="/images/banner.jpg" alt="Banner" width={1200} height={600} layout="constrained" />
    ```

    `layout` picks the `srcset` widths, and each one becomes a URL such as `/_astro/photo.a1b2c3.jpg?width=828&quality=75`. The build copies each image once and resizes nothing.
  </Step>
</Steps>

## 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).


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