# Frontends

A static or server-rendered site served by Volcano, optionally on your own custom domain and serving functions under its own paths.

## What it is

A deployed frontend (static or server-rendered site) served by Volcano,
optionally reachable at your own custom domain.

## How it relates

- Belongs to a **project**.
- Consumes **variables** at build/runtime.
- Can have several **custom domains**, as many as the plan allows, each at or
  below a domain your account has [verified](https://docs.volcano.dev/cli/domains.md), with a certificate
  Volcano manages or your own (BYOC). Each one serves the frontend or redirects
  to another of its domains.
- Can forward paths to public [functions](https://docs.volcano.dev/cli/functions.md) through
  **function routes**.
- Can serve another frontend of the same project under a path, such as
  `/docs`, through **Frontend routes**, so one hostname runs several apps.
- Frontend custom domains, function routes, and Frontend routes can also be
  declared in the [declarative config](https://docs.volcano.dev/cli/project-configuration.md).

`volcano frontends …` targets local development and `volcano cloud frontends …`
targets the cloud project. The local server builds and serves each frontend at
the `Site URL` that `frontends get` prints. A local deploy prints that URL too,
except for a new frontend, which has a URL only once its first build finishes.
Custom domains are a cloud command.

## CLI operations

| Operation | Local | Cloud |
|---|---|---|
| Deploy | `volcano frontends deploy …` | `volcano cloud frontends deploy …` |
| Redeploy | `volcano frontends redeploy …` | `volcano cloud frontends redeploy …` |
| Deploy another frontend's build | — | `volcano cloud frontends deploy <name> --from <frontend>[@<deployment-id>]` |
| List | `volcano frontends list` | `volcano cloud frontends list` |
| Get | `volcano frontends get <name>` | `volcano cloud frontends get <name>` |
| Delete | `volcano frontends delete <name>` | `volcano cloud frontends delete <name>` |
| Logs | `volcano frontends logs <name>` | `volcano cloud frontends logs <name>` |
| Custom domains | — | `volcano cloud frontends domains list\|get\|add\|update\|remove …` |
| Function and Frontend routes | `volcano frontends routes list\|create\|update\|delete …` | `volcano cloud frontends routes list\|create\|update\|delete …` |
| Previews | `volcano frontends preview create\|list\|get\|deploy\|delete\|promote …` | `volcano cloud frontends preview create\|list\|get\|deploy\|delete\|promote …` |
| What a frontend serves | `volcano frontends serving <name>` | `volcano cloud frontends serving <name>` |
| Roll back a promotion | `volcano frontends rollback <name> [--to-production]` | `volcano cloud frontends rollback <name> [--to-production]` |

Cloud frontend deploys and redeploys use latest-wins queueing. A new request replaces
older queued work while the current deployment finishes. Delete supersedes
queued deploys and blocks later deploys until deletion finishes.

Use `--variable-scope all` to expose all project variables. Use
`--variable-scope scoped` with a repeatable `--variable NAME` flag to expose
selected variables. A scoped deploy with no `--variable` flags exposes no
project variables. Omitting both flags preserves the current selection when the
frontend exists.

Builds never see variables named `VOLCANO_*`; the server runtime still gets
them. Browser code reads values Next.js inlines at build time, so give it
`NEXT_PUBLIC_*` copies, such as `NEXT_PUBLIC_VOLCANO_ANON_KEY`. See
[Variables](https://docs.volcano.dev/platform/frontends/overview.md#variables).

`volcano cloud config deploy` never creates a frontend; an entry for one that
does not exist yet is skipped. Deploy a new frontend with the variables its
manifest entry selects, then run `volcano cloud config deploy`.
[`volcano deploy`](https://docs.volcano.dev/cli/deploy.md) does both for every frontend listed in
`.volcano/source-layout.json`.

The selected variables other than `NEXT_PUBLIC_*` must fit the frontend's
4,096-byte runtime environment, of which Volcano uses about 900 bytes. See
[Environment size](https://docs.volcano.dev/platform/frontends/deploy.md#environment-size).

`deploy` archives `--path` (default `.`) and leaves out `node_modules`, `.next`,
`dist`, `build`, `.env*.local`, the Volcano env files (`volcano/*.env` and
`volcano.env`), `.git`, editor files, `*.log`, and anything in the root
`.gitignore`. Symbolic links are skipped with a warning, and more than
10,000 files fails the deploy. When the app uses `workspace:` dependencies,
`deploy` archives the workspace root instead, so these rules and the file limit
apply to the whole workspace; pass `--app-root` to archive only `--path`.

## Examples

```bash
# Deploy a frontend to local development, then print its local URL once built
volcano frontends deploy --name web --path ./web
volcano frontends get web

# Deploy / redeploy a frontend in the cloud
volcano cloud frontends deploy
volcano cloud frontends deploy --variable-scope scoped --variable API_URL --variable API_KEY
volcano cloud frontends redeploy my-site

# Inspect
volcano cloud frontends list
volcano cloud frontends logs my-site

# Attach and check a custom domain
volcano cloud frontends domains add my-site app.example.com
volcano cloud frontends domains get my-site app.example.com
```

## Custom domains

```bash
# Serve the frontend on www, with a certificate Volcano issues
volcano cloud frontends domains add my-site www.example.com

# Once www is active, send the apex to it, keeping the path and query string
volcano cloud frontends domains add my-site example.com --redirect-to www.example.com

# Or serve your own certificate
volcano cloud frontends domains add my-site app.example.com \
  --cert ./fullchain-leaf.pem --key ./privkey.pem

volcano cloud frontends domains list my-site
volcano cloud frontends domains get my-site example.com
```

```text
Domain                                            TLS       Status                  Created          Redirects to
--------------------------------------------------------------------------------------------------------------------------------
www.example.com                                   managed   active                  2m ago           -
example.com                                       managed   active                  1m ago           www.example.com (308)

Total: 2 custom domain(s)
```

Without `--cert` and `--key`, Volcano issues and renews the certificate once you
publish the DNS records that `add` and `get` print. A BYOC domain needs a
[verified domain](https://docs.volcano.dev/cli/domains.md) above it first; when none exists, `add` fails and
prints the TXT record to publish.

Running `add` again in the same TLS mode leaves the domain as it is, so it does
not replace a BYOC certificate. To rotate one, put the new PEM fields in the
domain's `tls` in the [declarative config](https://docs.volcano.dev/cli/project-configuration.md) and run
`volcano cloud config deploy`. The new certificate replaces the old one without
downtime. Use the domain's `custom_domains` entry, keeping the frontend's other
entries in the list, or `custom_domain` when the frontend has one domain and no
redirect.

`get` shows the DNS routing target hostname. Point every domain of the frontend
at it, including the ones that redirect: the browser connects over HTTPS before
it reads the redirect. Configure a CNAME only if your DNS provider confirms that
your domain is not a zone apex. At an apex, use a provider-supported ALIAS,
ANAME, or CNAME-flattening record with that target.

### Redirects

`--redirect-status` takes 301, 302, 307, or 308. `add` defaults to 308;
`update` keeps the domain's current status, or uses 308 for a domain that
served the frontend. 301 and 308 are permanent and browsers cache them; use 302
while you test. A redirect
must point at an `active` domain of the same frontend that serves it, not at
another redirect or a domain still waiting for DNS or its certificate. If the
target stops serving, the redirect domain serves the frontend until the target
is active again.

```bash
# Change the status, or make the domain serve the frontend again
volcano cloud frontends domains update my-site example.com --redirect-to www.example.com --redirect-status 302
volcano cloud frontends domains update my-site example.com --no-redirect

volcano cloud frontends domains remove my-site example.com
```

A domain that others redirect to cannot be removed or become a redirect itself;
Volcano refuses with `409` and names them. Retarget or remove those first. To
swap which domain serves in one step, declare both in `custom_domains` in the
[declarative config](https://docs.volcano.dev/cli/project-configuration.md) and run
`volcano cloud config deploy`.

Adding a domain past the plan's limit per frontend fails with `403`. On HOBBY,
managed certificates are also limited to one per account, so a second managed
domain fails with `403` on any frontend. See
[Plans and limits](https://docs.volcano.dev/platform/guides/plans-and-limits.md#frontends).

### Switch TLS modes

Run `add` for an active domain in the other mode to switch it in place, without
downtime. A domain that redirects keeps its redirect:

```bash
# BYOC to managed: your certificate serves until Volcano issues its own
volcano cloud frontends domains add my-site app.example.com

# Managed to BYOC: your certificate replaces the managed one at once
volcano cloud frontends domains add my-site app.example.com \
  --cert ./fullchain-leaf.pem --key ./privkey.pem
```

On a server without the `domains` commands, use
`volcano cloud frontends domain create my-site --domain app.example.com` with
`--tls managed` or `--cert` and `--key` instead.

Moving to managed TLS prints the `_acme-challenge` CNAME to publish.
`Serving certificate` reads `uploaded` until the managed certificate serves,
then `managed`. If issuance fails, `Failure reason` names the cause while your
certificate keeps serving. The switch needs a free managed certificate in your
[plan](https://docs.volcano.dev/platform/guides/plans-and-limits.md#frontends). A domain that is not yet
active has no certificate to keep serving. Wait until it is active, or delete
it and attach it again in the other mode.

If the frontend's entry in `volcano-config.yaml` declares the domain's `tls`,
in `custom_domain` or its `custom_domains` entry, set it to the new mode too. Otherwise the next `volcano cloud config deploy` or
`volcano deploy` switches the domain back. `mode: byoc` without PEM fields
keeps the certificate you uploaded.

Several domains per frontend are rolling out. Until they reach your project, a
frontend holds one domain: `domains add` refuses a second one with `403` and
`domain create` with `409`, and an older server answers the `domains` commands
with `404`. The `volcano cloud frontends domain
create|list|get|delete` commands work on every server and act on the
frontend's oldest domain that serves it.

## Deploy one build to several frontends

`--from` deploys the build another frontend of the project already serves,
without uploading or compiling again. Both frontends must exist:

```bash
# Deploy the build web serves to web-eu
volcano cloud frontends deploy web-eu --from web

# Deploy one particular deployment of web
volcano cloud frontends deploy web-eu --from web@3f6c2a1e-9b0d-4c51-8a39-0c6f2f1e8d42
```

```text
✓ Frontend 'web-eu' deployment started with the build of 'web'
Source deployment: 3f6c2a1e-9b0d-4c51-8a39-0c6f2f1e8d42
Deployment: 8d0e4b7a-1c2f-4e6d-9a3b-5f1e0c2b7a64
```

Without `@`, the CLI picks the newest `active` or `degraded` deployment of the
source frontend. `--from` takes no `--path`, `--name`, `--framework`,
`--app-root`, or variable flags: the target keeps its own settings, and the
build must match them. The deploy is refused when a build-time variable would have a different
value on the target, naming the variables:

```text
build variables differ from the source build: NEXT_PUBLIC_API_URL
```

Redeploy the source if those variables changed since its build, or give the
target the same variable selection. A deployment from before Volcano kept
builds is refused until the source is redeployed once. Local development
builds each frontend from its own source, so `volcano frontends deploy --from`
fails and names the command to run instead. See
[Deploy one build to several frontends](https://docs.volcano.dev/platform/frontends/deploy.md#deploy-one-build-to-several-frontends)
for what the target serves and what it costs.

## Previews

A preview is a copy of a frontend deployed to its own URL, so you can check a
change with the frontend's real variables before it replaces what the frontend
serves. Previews work on the local development server and in the cloud: use
`volcano frontends …` locally and `volcano cloud frontends …` in the cloud.

```bash
# Deploy a preview of web from a local directory, then read its URL when ready.
# The same commands run on the local development server without "cloud".
volcano cloud frontends preview create web --name redesign --path ./web --ttl 72h
volcano cloud frontends preview get web redesign
volcano cloud frontends preview list web

# Deploy a newer build to the same preview
volcano cloud frontends preview deploy web redesign --path ./web

# Make web serve the preview, check what it serves, and undo it
volcano cloud frontends preview promote web redesign
volcano cloud frontends serving web
volcano cloud frontends rollback web

# Remove the preview
volcano cloud frontends preview delete web redesign
```

`--name` is required and unique within the frontend; `--ttl` deletes the preview
after that long, at most 720 hours. A frontend can have 20 live previews.
Promotion routes the frontend's own host at the preview without rebuilding, and
is refused when the preview was built with different variables than the
frontend selects now. `promote` and `rollback` name the revision the CLI just
read, so one that races another is refused instead of overwriting it.
`rollback --to-production` returns to the frontend's own build.

While a preview is promoted, deploying the frontend itself is refused: roll
back first. A preview the frontend serves cannot be deleted; one it only keeps
for `rollback` can be, which gives up that rollback. Its URL is not
indexed, but anyone who holds it can open it. Creating, deploying to and
promoting a preview, and a rollback that would make one serve again, are behind
a release gate and answer `feature_unavailable` while it is off;
`rollback --to-production` always works. See
[Frontend previews](https://docs.volcano.dev/platform/frontends/previews.md).

## Function routes

A function route sends every request under a path of the frontend, such as
`/api/session`, to an HTTP-mode function. The browser stays on the frontend's
origin, so the function can keep a session in cookies.

```bash
# Forward /api/session to the session function; it sees /me for /api/session/me
volcano cloud frontends routes create web --path /api/session --function session --strip-prefix

# List them in match order, with each target's visibility
volcano cloud frontends routes list web

# Point the route at another function, or stop stripping the prefix
volcano cloud frontends routes update web /api/session --function session-v2
volcano cloud frontends routes update web /api/session --strip-prefix=false

# Stop forwarding the path
volcano cloud frontends routes delete web /api/session
```

`update` and `delete` take the route's path prefix or its ID. `update` keeps
whatever you leave out. `routes list` shows function and
[Frontend routes](#frontend-routes) together, longest path first, with each
route's kind. `frontends get` lists the routes too:

```text
Function routes:
  /api/session -> session (public, strip prefix)
```

The target must be a `public` standard function with `invocation_mode: http`.
A route reaches it with no Volcano credential, so anyone who can load the
frontend can call it, and the function must authenticate its callers itself.
Volcano refuses a route to a `private` or `authenticated` function, and refuses
to make a routed function non-public until its routes are deleted; the CLI
prints the command that fixes each. A frontend can have up to 64 routes,
function and Frontend routes together, and the longest matching prefix wins.

A path prefix starts with `/`, does not end with `/`, has no `?`, `#`, or `\`,
and is 2 to 512 characters long. The CLI checks this before calling Volcano.

The same commands without `cloud` manage routes on a local frontend. Declare
`function_routes` in [`volcano-config.yaml`](https://docs.volcano.dev/cli/project-configuration.md) to manage
the whole set at once. See
[Frontend Function routes](https://docs.volcano.dev/platform/frontends/function-routes.md) for how
requests are matched and how to keep a session in cookies.

## Frontend routes

A Frontend route mounts another frontend of the same project at a path of the
frontend. With `docs` mounted at `/docs` on `web`, `web`'s hostnames, custom
domains included, keep serving `web`, while every request under `/docs` is
served by `docs`.

```bash
# Serve /docs and everything under it from the docs frontend
volcano cloud frontends routes create web --path /docs --frontend docs

# List function and Frontend routes together
volcano cloud frontends routes list web

# Mount another frontend at the same path, or move the route
volcano cloud frontends routes update web /docs --frontend docs-v2
volcano cloud frontends routes update web /docs --path /guides

# Stop serving the path from docs
volcano cloud frontends routes delete web /docs
```

`frontends get` lists them after the function routes:

```text
Frontend routes:
  /docs -> frontend docs
```

The path reaches the mounted frontend unchanged: `/docs/intro` arrives as
`/docs/intro`, so build that frontend with its pages under the prefix, in
matching page directories or with a matching `basePath`. `--strip-prefix` is
for function routes only. A frontend can be mounted at several prefixes but
can't mount itself. Function and Frontend routes share one set of paths: two
routes can't use the same prefix, and the longest matching prefix wins whatever
its kind. A route keeps its kind, so `update` refuses `--function` on a
Frontend route and `--frontend` on a function route; delete the route and
create the other kind instead.

A frontend that another frontend mounts can't be deleted. `frontends delete`
reports the refusal with the frontends that mount it and prints the
`routes delete` command for each route to remove first.

The same commands without `cloud` manage Frontend routes on a local frontend.
Declare `frontend_routes` in [`volcano-config.yaml`](https://docs.volcano.dev/cli/project-configuration.md#frontend-routes)
to manage the whole set at once. See
[Frontend routes](https://docs.volcano.dev/platform/frontends/frontend-routes.md) for how a mounted
frontend's build files and images are served and how to link between
frontends.
