Skip to content

Octane

Archil.Website.Octane deploys a Octane app to an Archil sandbox, Octane’s SSR server, with client assets served first. 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 the package’s /octane and /octane/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

Keep native compiler and route settings in octane.config.ts, without an adapter:

octane.config.ts
import { defineConfig, RenderRoute } from "@octanejs/vite-plugin";
export default defineConfig({
router: {
routes: [new RenderRoute({ path: "/", entry: ["App", "/src/App.tsx"] })],
},
});

Archil.Website.Octane selects hosting and automatically wraps Octane’s default native Node output as an HTTP server. The legacy Node marker adapter remains optional for existing projects.

Vite plugins like Tailwind go in vite.config.ts next to it, alongside the Octane plugin:

vite.config.ts
import { octane } from "@octanejs/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [octane(), tailwindcss()],
});

Declare the site as a module-level const:

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

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

alchemy.run.ts
export const Website = Archil.Website.Octane("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.Octane("Website");
export default Alchemy.Stack(
"MyOctaneSite",
{
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-octane for the checked-in example.

Process environment is configured under env — plain strings, or Redacted values. They are copied onto process.env before the build and onto the program at deploy time:

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

The same values are injected into the dev server’s process environment under alchemy dev, so server code reads them in both modes.

Octane’s server runs in Node in the sandbox, so middleware and ServerRoute handlers read the environment from process.env:

octane.config.ts
new ServerRoute({
path: "/api/greeting",
methods: ["GET"],
handler: async () => {
return Response.json({ greeting: process.env.GREETING ?? "hello" });
},
});

Octane’s intended setup is asset-first with SSR on miss: exact files in dist/client are served by the Node process without invoking the Octane handler, and every miss streams from Octane SSR. That is the default — there is nothing to configure. GET /health answers for the program’s health check.

Terminal window
bun alchemy dev

alchemy dev runs Octane’s own Vite dev server — the plugin’s in-process SSR middleware serves rendering, server routes, and RPC with full HMR. site.url is the local address and no Archil resources are created (sandbox is undefined). Wrap the site in Alchemy.remote() to deploy the real infrastructure even during dev.