Websites
Alchemy deploys frontends to Hetzner with a family of
Hetzner.Website resources. Each one builds your project
programmatically and deploys it as a systemd
Service on a
Server — Node on port 3000, no
Dockerfile, no unit file, no adapter in the framework config.
Your framework’s own config file (vite.config.ts,
astro.config.*, nuxt.config.ts, …) loads natively; Alchemy
layers its Node server on top.
Omit server and Alchemy creates a cpx12 / ubuntu-24.04
Server in fsn1. Pass server to put several sites (or a site
plus an API) on one VM.
The live URL is http://{ipv4}:3000. There is no TLS on the
Service. domain requires an existing Hetzner DNS
Zone and creates an A record;
url becomes http://{domain}:3000.
Vite— client-only Vite apps (React, Vue, Solid SPAs,index.htmlmulti-page sites); a static-file unit serves thevite buildoutput.Astro— Astro sites, server-rendered on Node or fully static; yourastro.config.*loads natively.Nextjs— Next.js as a long-running Node process (next build, thennext({ dev: false })). Not OpenNext.Nuxt— Nuxt apps through nitro’s Node server; yournuxt.config.tsloads natively. Do not setnitro.preset.ReactRouter— React Router v7 in framework mode, built through your ownvite build.SolidStart— SolidStart 2.x SSR; Alchemy appends nitro’snodepreset to yourvite build.SvelteKit— SvelteKit SSR plus prerendered assets; the Node adapter is injected. Do not setkit.adapter.TanStackStart— TanStack Start (React or Solid), built through your ownvite build.Waku— Waku (React Server Components) on Node, with SSG pages served extensionless.Octane— OctaneJS SSR. Selectnode()from@alchemy.run/frontend-frameworks/octane/node-adapterinoctane.config.ts.Foldkit— client-only Foldkit SPA; deep links fall back toindex.html.Vocs— prerendered Vocs docs; extensionless pages (/about→about/index.html).StaticSite— any build command’s output directory (Hugo, Zola, Eleventy).
The return is { url, server, service } — server and
service are undefined under alchemy dev.
Shared props: rootDir, memo, env (process environment on
the unit — not Worker bindings), dev, domain, zone,
server. Builds are memoized by content-hashing the input
files — an unchanged project skips the build.
What’s supported
Section titled “What’s supported”| Framework | Resource | Guide |
|---|---|---|
| React / Vue / Solid SPA | Vite |
Vite |
| TanStack Start (React & Solid) | TanStackStart |
TanStack Start |
| React Router | ReactRouter |
React Router |
| SolidStart | SolidStart |
SolidStart |
| Astro | Astro |
Astro |
| Next.js | Nextjs |
Next.js |
| Nuxt | Nuxt |
Nuxt |
| SvelteKit | SvelteKit |
SvelteKit |
| Waku | Waku |
Waku |
| OctaneJS | Octane |
Octane |
| Foldkit | Foldkit |
Foldkit |
| Vocs | Vocs |
Vocs |
| Zola, Hugo, or any static generator | StaticSite |
Static sites |
See examples/hetzner-website-vite for a Vite SPA sharing a Server with an API unit.
How to choose
Section titled “How to choose”Use the resource named after your framework. Astro, Nextjs,
Nuxt, ReactRouter, SolidStart, SvelteKit,
TanStackStart, Waku, Octane, Foldkit, and Vocs each
drive that framework’s programmatic build and know its output
layout, config surface, and dev server.
Use Vite when the whole deployable output is static assets — a
React, Vue, or Solid SPA, or an index.html-per-route multi-page
app. SSR frameworks that wrap Vite (Astro, SvelteKit,
Octane, …) have their own resources; Vite never starts a
framework server module.
Use StaticSite when the build is an arbitrary shell command
that emits a directory of files.
Pass one Hetzner.Server into several Website (and API)
resources when they should share a VM.
Environment variables
Section titled “Environment variables”Top-level env is copied onto process.env before build and
dev, and onto the systemd unit at deploy. Client inlining is
whatever the framework already does (VITE_* for Vite). It is
not Worker bindings and not AWS server.environment.
const site = yield* Hetzner.Website.Vite("Web", { server, env: { VITE_API_URL: api.url, },});Server code reads process.env.
Local development
Section titled “Local development”alchemy dev runs the framework’s own dev server (native HMR)
instead of deploying. site.url is the local address and no
Server or Service is created. Wrap the site in
Alchemy.remote() to deploy the live path even during dev.
Custom domain
Section titled “Custom domain”domain requires zone — an existing Hetzner DNS Zone. v1
does not create a Zone. Alchemy adds an A
RecordSet pointing at the Server’s
public IPv4. Passing domain without zone fails.
const site = yield* Hetzner.Website.Astro("Web", { server, domain: "app.example.com", zone,});url is http://app.example.com:3000. The Service itself does
not terminate TLS.
Where next
Section titled “Where next”- The Vite resource — assets-only SPA,
assets.notFoundHandling, sharing a Server. - The StaticSite resource — build commands, SPA/404 handling.
- Framework guides: Astro, Next.js, Nuxt, React Router, SolidStart, SvelteKit, TanStack Start, Waku, Octane, Foldkit, Vocs.
- Servers and Services — the VM and systemd unit these resources deploy.
- Zones & records — the Zone
domainattaches to. Vitereference,Astroreference,StaticSitereference.