---
title: "Dashboard"
description: "Configure and serve the Pug dashboard against your self-hosted API."
---

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

# Dashboard

The Pug dashboard — the React UI where you explore analytics and manage projects — is a separate repo from the backend: [**`pug-sh/app`**](https://github.com/pug-sh/app) ("Pug UI"). It's a **static single-page app** (React + Vite, ConnectRPC, Tailwind) that talks to your `pug server` over Connect RPC. There's no server runtime to operate: you build it to a folder of static files and serve them from any web server or CDN.

```bash
git clone git@github.com:pug-sh/app.git
cd app
```

## 1. Configure (build time)

The dashboard's configuration is **baked in at build time** via `VITE_*` environment variables — they're compiled into the JavaScript bundle, not read at runtime. Copy `.env.example` to `.env.production` (Vite loads it for production builds) and set the values, or export them in your CI before `bun run build`.

| Variable | Required? | Default | Description |
|----------|-----------|---------|-------------|
| `VITE_API_BASE_URL` | **Yes** | `http://localhost:3000` | Your `pug server`'s public URL. This is the one that makes it *your* instance — point it at the backend you deployed (e.g. `https://api.example.com`). |
| `VITE_GOOGLE_CLIENT_ID` | No | _(empty)_ | Google OAuth client ID for "Sign in with Google". Leave empty to hide the button. **Must match the backend's `PUG_OAUTH_GOOGLE_CLIENT_ID`.** |
| `VITE_MAP_ASSETS_URL` | No | `https://maps.pug.sh` | Base URL for the dashboard's self-hosted map assets (basemap tiles, fonts, boundary data). Defaults to Pug's public CDN — see [Map assets](#map-assets). |
| `VITE_DEMO_ENABLED` | No | _(empty)_ | Cosmetic — set to `true` to advertise an "Explore the live demo" link on the sign-in page. The real demo switch is the backend's `PUG_DEMO_ENABLED`. |

> Because these are compiled in, **changing a value means rebuilding** (or re-templating the built assets at deploy). If you serve the same build to multiple environments, build once per environment.

## 2. Build

```bash
bun install
bun run build
```

`bun run build` type-checks and produces a static bundle in **`dist/`** — plain HTML, JS, and CSS with no server component. That folder is your entire deployable.

## 3. Serve

Serve the contents of `dist/` from any static host — Cloudflare Pages, Netlify, S3 + CloudFront, nginx, Caddy. Two requirements:

- **SPA fallback (history mode).** The dashboard uses client-side routing, so the host must serve `index.html` for any unmatched path (e.g. Cloudflare Pages does this automatically; for nginx use `try_files $uri /index.html`). Without it, deep links and refreshes 404.
- **HTTPS.** The dashboard sends a JWT to the backend; serve it over TLS.

## 4. Wire it to the backend

The dashboard and backend are separate origins (e.g. `https://app.example.com` and `https://api.example.com`), so two backend settings must know about the dashboard — set these in the [backend configuration](/self-hosting/configuration):

| Backend variable | Set it to | Why |
|------------------|-----------|-----|
| `PUG_CORS_ORIGINS` | your dashboard origin | The browser blocks the dashboard's API calls otherwise. Don't use `*` — it disables the credentialed requests sign-in needs. |
| `PUG_DASHBOARD_BASE_URL` | your dashboard origin | So magic-link and invite emails link back to your dashboard. |

If you enabled Google sign-in, the same client ID goes in both places: `VITE_GOOGLE_CLIENT_ID` (dashboard) and `PUG_OAUTH_GOOGLE_CLIENT_ID` (backend).

## Map assets

The dashboard renders maps from a set of static assets — a basemap tile archive, label fonts, and boundary data — rather than a third-party tile server. `VITE_MAP_ASSETS_URL` points at where those assets are hosted and defaults to Pug's public CDN (`https://maps.pug.sh`), which works out of the box; leave it as-is for most self-hosts.

If you can't reach that CDN (air-gapped, or by policy), mirror the assets to your own object store or CDN — it must allow CORS and honor HTTP Range requests — and point `VITE_MAP_ASSETS_URL` there. Either way, the dashboard works without reachable map assets: only the map visualizations are affected, everything else renders normally.

## Local development

Run the dashboard against your local backend with Vite's dev server:

```bash
bun install
bun run dev        # http://localhost:5173
```

The default `VITE_API_BASE_URL` (`http://localhost:3000`) already matches a local [`pug dev`](/self-hosting/development) backend, so the two line up with no extra config. Full local walkthrough: [Development](/self-hosting/development).

## Further reading

- [Deployment](/self-hosting/deployment) — deploy the backend the dashboard talks to
- [Connect your SDKs](/self-hosting/connect-sdks) — point your apps at your instance
- [Configuration](/self-hosting/configuration) — the backend `PUG_CORS_ORIGINS` / `PUG_DASHBOARD_BASE_URL` settings

Source: https://docs.pug.sh/self-hosting/dashboard/index.mdx
