1---
2title: SvelteKit
3description: Deploy a SvelteKit app to Hetzner with Hetzner.Website.SvelteKit — kit SSR as a systemd unit on port 3000, prerendered assets baked into the unit, and Kit's own dev server under alchemy dev.
4sidebar:
5 order: 12
6---
7
8import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
9
10export const devDeps = "@alchemy.run/frontend-frameworks";
11
12`Hetzner.Website.SvelteKit` deploys a
13[SvelteKit](https://svelte.dev/docs/kit) app to a Hetzner Cloud
14Server. It builds the app with SvelteKit's own Vite pipeline and an
15in-memory Node adapter. Client assets and prerendered pages bake into
16the systemd unit; dynamic routes run in the kit handler on port 3000.
17Alchemy creates a `cpx12` / `ubuntu-24.04` Server in `fsn1` if you
18omit `server`, then a `Hetzner.Service` on that Server. The live URL
19is `http://{ipv4}:3000`. Your `vite.config.ts` loads natively; there
20is no `svelte.config.js` to write (kit v3 dropped it) and no adapter
21to install.
22
23## Install
24
25The build integration is not bundled with alchemy. Install
26`@alchemy.run/frontend-frameworks`; the resource loads its
27`/sveltekit` and `/sveltekit/node` exports from your project at
28deploy time. It is only used at build time, so a dev dependency is
29enough:
30
31<Tabs syncKey="pkgManager">
32 <TabItem label="bun" icon="bun">
33 <Code code={`bun add -d ${devDeps}`} lang="sh" />
34 </TabItem>
35 <TabItem label="npm" icon="npm">
36 <Code code={`npm install -D ${devDeps}`} lang="sh" />
37 </TabItem>
38 <TabItem label="pnpm" icon="pnpm">
39 <Code code={`pnpm add -D ${devDeps}`} lang="sh" />
40 </TabItem>
41 <TabItem label="yarn" icon="seti:yarn">
42 <Code code={`yarn add -D ${devDeps}`} lang="sh" />
43 </TabItem>
44</Tabs>
45
46## Configure SvelteKit
47
48Your project's `vite.config.ts` loads natively — your Vite plugins
49and the kit options in your `sveltekit(...)` call all apply as usual.
50Alchemy injects its Node adapter (replacing any adapter you declare,
51with a warning), so a fresh SvelteKit project deploys as-is.
52Deploy-specific kit overrides can also be passed via the `kit` prop,
53which merges over your `sveltekit(...)` options — see
54[Kit options](#kit-options) below. A project without a
55`vite.config.*` works too: the resource falls back to a fully
56programmatic build.
57
58## Declare the Website
59
60Declare the site as a module-level const (rather than inline in the
61Stack):
62
63```typescript
64// alchemy.run.ts
65import * as Hetzner from "alchemy/Hetzner";
66
67export const Website = Hetzner.Website.SvelteKit("Website");
68```
69
70Omit `server` to create a `cpx12` in `fsn1`. Pass an existing Server
71so several sites or APIs share the VM.
72
73## Add it to the Stack
74
75Yield the site from your Stack and return its URL:
76
77```typescript
78// alchemy.run.ts
79import * as Alchemy from "alchemy";
80import * as Effect from "effect/Effect";
81
82export default Alchemy.Stack(
83 "MySvelteKitSite",
84 {
85 providers: Hetzner.providers(),
86 state: Alchemy.localState(),
87 },
88 Effect.gen(function* () {
89 const site = yield* Website;
90 return { url: site.url };
91 }),
92);
93```
94
95Routes with `export const prerender = true` are baked into the unit
96and served as static files first; server routes (`+server.ts`) and
97server load functions run in the kit handler on the Server.
98
99## Add environment variables
100
101Process environment is a top-level `env` map:
102
103```diff lang="typescript"
104// alchemy.run.ts
105export const Website = Hetzner.Website.SvelteKit("Website", {
106+ env: {
107+ GREETING: "Hello from alchemy",
108+ API_BASE: "https://api.example.com",
109+ },
110});
111```
112
113The values are copied onto `process.env` before the build (and the
114dev server) and onto the systemd unit at deploy time, so server code
115reads the same keys in both modes.
116
117## Read the environment in server code
118
119The Kit server runs in Node on the Server, so server routes read the
120environment from `process.env` (or kit's `$env/dynamic/private`,
121which is initialized from it):
122
123```typescript
124// src/routes/+page.server.ts
125export const load = () => {
126 return { greeting: process.env.GREETING ?? "hello" };
127};
128```
129
130## Kit options
131
132Since kit v3 there is no `svelte.config.js` — kit options live in the
133`sveltekit(...)` call in your `vite.config.ts`, which loads natively:
134
135```typescript
136// vite.config.ts
137import { sveltekit } from "@sveltejs/kit/vite";
138import tailwindcss from "@tailwindcss/vite";
139import { defineConfig } from "vite";
140
141export default defineConfig({
142 plugins: [
143 tailwindcss(),
144 sveltekit({
145 alias: { $lib: "src/lib" },
146 }),
147 ],
148});
149```
150
151The `kit` prop on the resource is a deploy-time override layer merged
152over those options (the override wins). It must be JSON-serializable:
153
154```typescript
155// alchemy.run.ts
156export const Website = Hetzner.Website.SvelteKit("Website", {
157 kit: {
158 paths: { base: "/docs" },
159 },
160});
161```
162
163Don't set `kit.adapter` — Alchemy injects its Node adapter (an
164adapter declared in your `sveltekit(...)` call is replaced with a
165warning).
166
167## Local dev
168
169```sh
170bun alchemy dev
171```
172
173`alchemy dev` runs SvelteKit's own Vite dev server — Node SSR with
174full HMR — instead of deploying; `site.url` is the local address and
175no Hetzner resources are created (`server` and `service` are
176`undefined`). Wrap the site in `Alchemy.remote()` to deploy the live
177Server even during dev.
178
179## Custom domain
180
181`domain` requires an existing [`Hetzner.Zone`](/hetzner/networking/dns).
182Alchemy creates an A RecordSet pointing at the Server's public IPv4.
183There is no TLS on the Service — `url` is `http://{domain}:3000`.
184
185```typescript
186const zone = yield* Hetzner.Zone("dns", { name: "example.com" });
187
188const site = yield* Hetzner.Website.SvelteKit("Web", {
189 domain: "app.example.com",
190 zone,
191});
192```
193
194Passing `domain` without `zone` fails.
195
196## Where next
197
198- [`SvelteKit` API](/providers/hetzner/website/sveltekit)
199- [Servers](/hetzner/compute/servers) and
200 [Services](/hetzner/compute/services)
201- [DNS](/hetzner/networking/dns)