> ## 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 Angular to Bunny Storage > Build an Angular site and serve it from Bunny Storage through Bunny CDN, with the Bunny CLI or by hand. The Angular application builder writes your app to `dist//browser`, where `` is the name in `angular.json`. | Setting | Value | | - | - | | Build command | `npm run build` | | Output directory | `dist//browser` | ## Configure Angular The Bunny CLI deploys `dist` for Angular by default. Point it at the `browser` folder in `bunny.jsonc` at your project root: ```jsonc bunny.jsonc theme={null} { "sites": { "dir": "dist/my-app/browser" } } ``` ## Deploy your site > [tab] Bunny CLI > > > [!WARNING] > > > > `bunny sites` is in [Public Preview](https://bunny.net/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](https://bunny.net/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. > [tab] 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](https://bunny.net/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://.b-cdn.net`. ## Resize images with Bunny Optimizer `NgOptimizedImage` builds a `srcset` from a custom image loader. Point the loader at [Bunny Optimizer](https://bunny.net/docs/optimizer/dynamic-images/overview.md), and the edge resizes each width. **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](https://bunny.net/docs/cdn/vary-cache.md#url-query-string) vary, so each `?width=` gets its own cache entry. * Enable [Bunny Optimizer](https://bunny.net/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. Provide the loader** Add an `IMAGE_LOADER` provider to your app config: ```ts src/app/app.config.ts theme={null} import { ApplicationConfig } from "@angular/core"; import { IMAGE_LOADER, ImageLoaderConfig } from "@angular/common"; export const appConfig: ApplicationConfig = { providers: [ { provide: IMAGE_LOADER, useValue: ({ src, width }: ImageLoaderConfig) => { const url = new URL(src, "http://localhost"); if (width) url.searchParams.set("width", String(width)); return url.pathname + url.search; }, }, ], }; ``` **3. Add images** Import `NgOptimizedImage` in your component, then use `ngSrc`: ```html theme={null} ``` With `sizes`, Angular writes a `srcset` with URLs such as `/images/photo.jpg?width=828`. Without it, you get `1x` and `2x` entries. ## Client-side routing Single-page apps handle routing in the browser, so a direct visit to a path like `/settings` needs to load `index.html`. The Bunny CLI turns this on for you when it detects a single-page app, unless your build includes a `404.html`. To change it, set `"spa"` in the `sites` block of `bunny.jsonc`, or pass `--spa` or `--no-spa` to `bunny sites deploy`. For a manual deploy, open your storage zone's **Error handling** page, set **404 File path** to `/index.html`, and check **Rewrite 404 to 200 status code**. See [Storage zone settings](https://bunny.net/docs/storage/settings.md#error-handling) for details. ## 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](https://bunny.net/docs/cdn/custom-hostname.md). This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.