SvelteKit¶
The Wodby SvelteKit catalog stack runs a Node.js (SvelteKit) app service with optional PostgreSQL, Valkey, Mailpit,
OpenSMTPD, Gotenberg, and OpenTelemetry Collector services. The app renders pages, runs form actions, and serves API
routes on a Node.js server. The current services and defaults are documented in
wodby/stack-sveltekit.
Create the environment¶
Start from the SvelteKit Boilerplate, or connect your own compatible source repository. The app service is the only required service. The others are optional and not selected by default, so select the ones your application uses when you create the environment. The app service links to PostgreSQL for the database, Valkey for Redis, Mailpit for mail, and the OpenTelemetry Collector for telemetry. Change a link under Connections > Links on the app service.
The app service's version is the Node.js version, and every version the service offers runs SvelteKit. The boilerplate
includes a health endpoint at /healthz and checks it after each deployment.
Build and start¶
- The service's Dockerfile copies your build context, runs
npm run buildwhile the image is built, and starts the container withnpm run start.package.jsonmust define both scripts. - Build with the Node.js adapter: set
adapter: adapter()from@sveltejs/adapter-nodein thesveltekit()plugin invite.config.ts, build withvite build, and start withnode build. SvelteKit does not readsvelte.config.js. The boilerplate is set up this way. - The Dockerfile does not install dependencies. Install them in your pipeline before the image is built, for example
with
wodby ci run -- npm ci, so thatnode_modulesis part of the image. See CI/CD. SvelteKit, the adapter, Vite, and Svelte can stay indevDependencies: the adapter bundles them intobuild/. - Runtime variables are not available to
npm run build. That includes link variables and secrets. See Environment variables for how to read them. - The application must listen on port
3000, the adapter's default. Do not setPORT. NODE_ENVisproductionin every environment type, includingdev, because the container runs a production build. Do not set it todevelopmentfor a deployed environment.- The image build leaves out
build/and.svelte-kit/and creates them again. - Request bodies are limited to 512 KB. For larger uploads, set
BODY_SIZE_LIMITon the app service, for example to10MorInfinity. - Files written inside the container are lost when it is replaced.
Environment variables¶
Declare the variables your application reads in src/env.ts with defineEnvVars from @sveltejs/kit/env, and import
them in server code from $app/env/private. Add your own variables to the app service, see
Environment variables.
- Keep variables dynamic, the default. A variable with
static: trueis fixed at build time, when runtime variables do not exist. - SvelteKit checks the declared variables during
vite buildas well as when the server starts. A variable declared without aschemamust be set, so the image build fails. Give every runtime variable a schema that accepts a missing value, such asschema: (value) => value || undefined, as the boilerplate'ssrc/env.tsdoes. - Variables declared with
public: truereach the browser through$app/env/public. Never make a secret public. - Server code can also read
process.envwhen a request is handled. The$env/dynamic/*and$env/static/*modules still work but are deprecated.
Domains and form actions¶
Wodby's gateway handles HTTPS and passes each request to the app with its original host name. The app service sets the
adapter's PROTOCOL_HEADER, ADDRESS_HEADER, and XFF_DEPTH for that, so:
url.originis the scheme and host name the visitor used, on every domain of the environment. A request without the scheme header, such as a check from inside the environment, is treated ashttps.- Form actions pass SvelteKit's origin check on every domain of the environment. Do not set
paths.origininvite.config.ts: it is fixed at build time and allows only one domain. Usecsrf.trustedOriginsonly for other sites that must post to the application. getClientAddress()returns the visitor's address. It throws for a request that did not pass the gateway, such as a check from inside the environment, so do not call it on every request inhandleor in/healthz.- Do not set
ORIGIN,HOST_HEADER, orPORT_HEADER. The Node.js adapter does not readORIGIN.
On shutdown, open requests get 25 seconds to finish, set by SHUTDOWN_TIMEOUT. Then the sveltekit:shutdown event
runs, before the container is stopped.
Connect the database, Valkey, and mail¶
Declare the variables your application uses in src/env.ts with a schema that accepts a missing value: the links are
optional, and the variables do not exist during the build. They do not reach the browser unless you declare them
public. A variable is present only while its link exists and the linked service is enabled. The mail link carries no
credentials.
| Link | Variables |
|---|---|
| Database | DB_HOST, DB_PORT, DB_NAME (also DB_DATABASE), DB_USERNAME, DB_PASSWORD, DB_DRIVER (also DB_CONNECTION) |
| Valkey | REDIS_HOST, REDIS_PORT, REDIS_PASSWORD |
SMTP_HOST, SMTP_PORT |
The database link sets no DATABASE_URL. If your database library needs a URL, build it from these variables in server
code and URL-encode the user name and password.
Valkey writes nothing to disk unless you add a data volume, and it can evict any key at its memory limit. For a job
queue or sessions, set VALKEY_MAXMEMORY_POLICY to noeviction on the Valkey service and size VALKEY_MAXMEMORY to
fit.
Gotenberg has no link. Its address is http://gotenberg:3000; put it in your own configuration. See the
Gotenberg guide.
Mail¶
Mailpit captures every message and delivers none, so use it for development and testing. Its public domains are protected by basic auth by default. Find the password under Endpoints > Auths, see Endpoints. Mailpit accepts SMTP without authentication or TLS, so configure the application's mail transport without them.
For real delivery, enable OpenSMTPD, point the app service's mail link to it, and attach an SMTP provider. See the OpenSMTPD guide.
Telemetry¶
When you select the OpenTelemetry Collector, the app service's link to it sets the standard OpenTelemetry SDK
variables OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL (http/protobuf), and
OTEL_EXPORTER_OTLP_HEADERS (the Collector's ingest token). An OpenTelemetry SDK in your application exports to the
Collector without further configuration. Set OTEL_SERVICE_NAME on the app service to name the application. The
Collector is reachable only inside the environment.
The Collector stores nothing. It forwards traces, metrics, and logs over OTLP/HTTP to another Collector or backend. On
the Collector app service, under Configuration > Settings, set Upstream OTLP endpoint to the receiver's base
URL and Upstream Authorization header to the header it expects, such as Bearer <ingest token>. See
Settings tab.
The Grafana Tempo, Loki, and
VictoriaMetrics stacks each accept one kind of data. Turn off the other exports in the
application, for example with OTEL_METRICS_EXPORTER=none and OTEL_LOGS_EXPORTER=none when you send to Tempo.
Data and backups¶
PostgreSQL stores data on a data volume of 10 GB. Its backups are gzipped SQL dumps of the environment's database. An
import replaces the contents of the data volume. See Application backups and
Application imports.
The app service has no volume, backup, or import of its own.
Development workspace¶
In a development workspace:
- Setup installs dependencies from the lockfile, which also runs the
preparescript (svelte-kit syncin the boilerplate). Run setup again after you change dependencies. - The app service runs the Vite development server through Vite's API with your
vite.config.ts, not with thedevscript inpackage.json. Options in that script do not apply.vitemust be a dependency of the project. - The server listens on port
3000. Do not change the port in your Vite configuration. - Vite rejects host names it does not know. Allow the environment's hosts in
server.allowedHostsofvite.config.ts.WODBY_HOSTSholds them as a JSON array; the boilerplate'svite.config.tsreads it. NODE_ENVisdevelopment. Saved files and new route files are picked up without a restart, and SvelteKit regenerates.svelte-kit/with the route types. Thenpm run buildstep does not run, and the origin check of form submissions is off.build/,.svelte-kit/, andnode_modulesare kept out of Git status.- To start the server another way, set
WORKSPACE_NODE_COMMANDon the app service. The command must listen on$HOST:$PORTand poll for file changes itself. - A change to variables or linked services still needs a deployment of the environment.