Vocs
Fly.Website.Vocs deploys a Vocs documentation
project to Fly. Vocs prerenders static HTML; the node target drops
server modules and Alchemy hosts a tiny static-file server on a Fly
Service — one App plus
a shared IPv4 so https://{app}.fly.dev answers. Extensionless
pages work (/about → about/index.html). Your vocs.config.*
loads natively — there is no adapter to install.
Install
Section titled “Install”The build integration is not bundled with alchemy. Install
@alchemy.run/frontend-frameworks; the resource loads
/vocs/node from your project at deploy time. It is only used at
build time, so a dev dependency is enough:
bun add -d @alchemy.run/frontend-frameworksnpm install -D @alchemy.run/frontend-frameworkspnpm add -D @alchemy.run/frontend-frameworksyarn add -D @alchemy.run/frontend-frameworksYour Vocs project already needs vocs installed.
Configure Vocs
Section titled “Configure Vocs”Your vocs.config.* file loads natively — title, sidebar, and any
other Vocs options work exactly as they do outside Alchemy:
import { defineConfig } from "vocs/config";
export default defineConfig({ title: "Docs", sidebar: [ { text: "Home", link: "/" }, { text: "Guide", link: "/guide" }, ],});If vocs.config.* sets outDir, pass the same value on the
resource (default "dist") so generated output stays outside the
rebuild hash:
export const Website = Fly.Website.Vocs("Website", { rootDir: "./docs", outDir: "build",});Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack):
import * as Fly from "alchemy/Fly";
export const Website = Fly.Website.Vocs("Website", { rootDir: "./docs",});Omit app to create a Fly.App under this site’s namespace. Pass
an existing Fly.App when the site should live on an App you
already declared.
Add it to the Stack
Section titled “Add it to the Stack”Yield the site from your Stack and return its URL:
import * as Alchemy from "alchemy";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "MyVocsSite", { providers: Fly.providers(), state: Alchemy.localState(), }, Effect.gen(function* () { const site = yield* Website; return { url: site.url }; }),);The live URL is https://{app}.fly.dev. The hosted server listens
on port 3000 and answers GET /health. site.app, site.service,
site.ip, and site.certificate are set on deploy.
Add environment variables
Section titled “Add environment variables”Top-level env is copied onto process.env before build and
alchemy dev, and onto the Machine at deploy:
export const Website = Fly.Website.Vocs("Website", { rootDir: "./docs", env: { DOCS_TITLE: "Hello from Alchemy!", },});These are process environment variables, not Worker bindings.
Read the environment
Section titled “Read the environment”Vocs prerenders at build time, so page content that reads
process.env is baked into the HTML. The same values are set on
the hosted Node process; there is no request-time Vocs server.
const title = process.env.DOCS_TITLE ?? "Docs";Local development
Section titled “Local development”alchemy dev runs Vocs’ own dev server (native HMR) instead of
deploying; site.url is the local address and no Fly resources are
created (app, service, ip, and certificate are
undefined). Wrap the site in Alchemy.remote() to deploy the
live App and Service even during dev:
export const Website = Fly.Website.Vocs("Website", { rootDir: "./docs",}).pipe(Alchemy.remote());Custom domain
Section titled “Custom domain”domain is a hostname string. Alchemy requests ACME
(Fly.Certificate) on the App; url becomes https://{domain}.
v1 expects DNS for that hostname to already point at the App:
export const Website = Fly.Website.Vocs("Website", { rootDir: "./docs", domain: "docs.example.com",});Where next
Section titled “Where next”Vocsreference — every prop and attribute- Apps and Services — the Machine the site runs on
- IPs & certificates — fly.dev IPv4 and ACME
- Fly — the Fly provider hub