Skip to content

Waku

Archil.Website.Waku deploys a Waku app to an Archil sandbox, (React Server Components) on Node, with SSG pages served extensionless. The site is the program of its own sandbox, on port 3000, at a public https://3000-…archil.app URL. A stopped sandbox starts again on the next request.

The build integration is not bundled with alchemy. Install @alchemy.run/frontend-frameworks; the resource loads its /waku and /waku/node exports from your project at deploy time. It is only used at build time, so a dev dependency is enough:

Terminal window
pnpm add -D https://pkg.alchemy.run/@alchemy.run/frontend-frameworks/pr:2930:d8eb8a5

No framework config is required — a fresh Waku project deploys as-is. If your project has a waku.config.ts, Alchemy loads it natively (exactly as Waku’s own CLI does) and uses it as the base config — including Vite plugins under its vite field:

waku.config.ts
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "waku/config";
export default defineConfig({
basePath: "/",
vite: {
plugins: [tailwindcss()],
},
});

Options set on the resource (srcDir, distDir, basePath) merge over the file, per key. The one key Alchemy owns is unstable_adapter — the Node deploy target sets it. Setting it fails the build with an actionable error.

Declare the site as a module-level const:

alchemy.run.ts
import * as Archil from "@alchemy.run/archil";
export const Website = Archil.Website.Waku("Website");

sandbox sizes the site’s sandbox and sets its image, timeouts, disks and network policy:

alchemy.run.ts
export const Website = Archil.Website.Waku("Website", {
sandbox: { vcpuCount: 2, memSizeMiB: 4096, idleTtlSeconds: 600 },
});

Yield the site from your Stack and return its URL:

alchemy.run.ts
import * as Archil from "@alchemy.run/archil";
import * as Alchemy from "alchemy";
import * as Effect from "effect/Effect";
export const Website = Archil.Website.Waku("Website");
export default Alchemy.Stack(
"MyWakuSite",
{
providers: Archil.providers(),
state: Alchemy.localState(),
},
Effect.gen(function* () {
const site = yield* Website;
return { url: site.url };
}),
);

Run alchemy deploy. url is the site’s https://3000-…archil.app URL. site.sandbox is the sandbox behind it, and undefined under alchemy dev.

See examples/archil-website-waku for the checked-in example.

Process environment is a top-level env map — plain strings, or Redacted values:

alchemy.run.ts
export const Website = Archil.Website.Waku("Website", {
env: {
GREETING: "Hello from alchemy",
API_BASE: "https://api.example.com",
},
});

The values are copied onto process.env before the build and the dev server, and onto the program at deploy time, so server code reads the same keys in both modes. Client inlining is whatever Vite does (VITE_ prefixes).

The RSC server is a plain Node process. Server components and API routes read process.env (Waku’s getEnv reads the same values):

src/pages/index.tsx
export default async function HomePage() {
const greeting = process.env.GREETING ?? "hello";
return <h1>{greeting}</h1>;
}
export const getConfig = async () => ({ render: "dynamic" }) as const;
Terminal window
bun alchemy dev

alchemy dev runs Waku’s own dev server (native HMR) instead of deploying; site.url is the local address and no Archil resources are created. Wrap the site in Alchemy.remote() to deploy the live sandbox even during dev:

export const Website = Archil.Website.Waku("Website").pipe(
Alchemy.remote(),
);