Skip to content

Astro

Archil.Website.Astro deploys a Astro app to an Archil sandbox, server-rendered on Node by default; pages that export const prerender = true are served as static files. 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 /astro and /astro/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

Your astro.config.* file loads natively — integrations, Vite plugins, and any other non-serializable options work exactly as they do outside Alchemy. Alchemy merges a programmatic config over it: the Node adapter is injected for you (don’t declare an adapter in your config — that fails the build with an actionable error), and options passed via the astro prop override the file’s — see Astro configuration below for the details.

Declare the site as a module-level const:

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

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

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

Process environment is the top-level env prop — plain strings, or Redacted values. They are copied onto process.env before build and alchemy dev, and onto the program at deploy:

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

This is the hosted Node process, not Worker bindings.

The Astro server runs in Node in the sandbox, so server-rendered pages and endpoints read the environment from process.env:

src/pages/index.astro
---
const greeting = process.env.GREETING ?? "hello";
---
<h1>{greeting}</h1>

Astro inlines PUBLIC_* keys at build time. Because env is applied before the build, those keys behave the same as in a local astro build.

With astro: { output: "static" } every page is prerendered at build time and the deploy is assets-only — no Astro handler, just a static-file server as the program:

export const Website = Archil.Website.Astro("Website", {
astro: { output: "static" },
assets: { notFoundHandling: "404-page" },
});

assets.notFoundHandling applies only to output: "static". "404-page" serves the built 404.html for unmatched routes; "single-page-application" answers misses with the index page (200) instead.

Your astro.config.* is the primary place to configure Astro — integrations, Vite plugins, and every other option (serializable or not) work as usual:

astro.config.ts
import { defineConfig } from "astro/config";
import react from "@astrojs/react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
site: "https://blog.example.com",
integrations: [react()],
vite: {
plugins: [tailwindcss()],
},
});

The astro prop on the resource exposes common serializable options for deploy-specific overrides; Astro merges them over the config file (scalars override, arrays like integrations and vite.plugins concatenate after the file’s):

alchemy.run.ts
export const Website = Archil.Website.Astro("Website", {
astro: {
site: "https://preview.example.com",
},
});

Two options are managed for you regardless of the config file:

  • adapter — Alchemy injects the Node adapter. Declaring an adapter in astro.config.* fails the build with an actionable error.
  • output — Alchemy defaults it to "server" (superseding a file-level output), so pages render on demand in the sandbox. Opt into a fully prerendered site with astro: { output: "static" } on the resource.

site, base, srcDir, publicDir, outDir, and trailingSlash are the other serializable keys on astro.

alchemy dev runs Astro’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 real sandbox even during dev.