Skip to content

Octane

AWS.Website.Octane deploys an OctaneJS fullstack app to AWS. Octane wraps Vite, so the resource is deliberately thin: it runs your project’s own vite build — Octane’s plugin builds the client bundle and the self-contained SSR server bundle, and the deploy target’s finishing pass wraps its fetch handler in a Node server. The server deploys on a Lambda Web Function; dist/client is served from a private S3 bucket through CloudFront. No CloudFormation, no build command to run.

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:a10573e

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"] })],
},
});

AWS.Website.Octane selects hosting and automatically wraps Octane’s default native Node output in a Node server. The legacy AWS marker adapter remains optional for existing projects.

Declare the site as a module-level const (rather than inline in the Stack):

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

Yield the site from your Stack and return its URL:

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

The server function’s environment is configured under env — plain values, or outputs from other resources in the Stack:

alchemy.run.ts
export const Website = AWS.Website.Octane("Website", {
env: {
GREETING: "Hello from alchemy",
API_BASE: api.url,
},
});

The values are set on the server at deploy time and injected into the dev server’s process environment under alchemy dev, so server code reads the same values in both modes.

Octane’s server is a plain Node server, 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 serve from S3 via the CloudFront edge router without invoking the server, and every miss streams from Octane SSR on the server. That is the default — there is nothing to configure.

Web Functions run in a subset of Regions. Elsewhere, set server: "function": the app builds with the /octane/aws deploy target and runs on a Lambda Function behind a streaming Function URL, tuned with memorySize, architecture, and runtime:

alchemy.run.ts
export const Website = AWS.Website.Octane("Website", {
server: "function",
memorySize: 2048,
});
Terminal window
pnpm 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 AWS resources are created. Wrap the site in Alchemy.remote() to deploy the real infrastructure even during dev.

const site = yield* AWS.Website.Octane("Web", {
domain: { name: "app.example.com" },
});

The hosted zone is inferred from the hostname. Pass hostedZoneId to pin it when several zones could match.