Deploy with adapter-node in Docker

adapter-node produces a standalone Node server: npm run build emits a build/ directory you run with node build. This is the self-host path, and the one that fits the lab’s Docker-first convention.

Install the adapter

npx sv add sveltekit-adapter="adapter:node"

or manually (note: in SvelteKit 3 the adapter config lives in the Vite plugin, not svelte.config.js):

// vite.config.js
import adapter from '@sveltejs/adapter-node';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
    plugins: [
        sveltekit({
            adapter: adapter(),
        }),
    ],
});

Build and run

npm run build
HOST=127.0.0.1 PORT=4000 node build

The server binds 0.0.0.0:3000 by default; PORT, HOST, and SOCKET_PATH override that. The build bundles dev dependencies via Rolldown - only dependencies stay external, so put runtime-only packages there.

Dockerfile

Multi-stage, distroless-style, matching the lab’s container conventions:

# Build.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Deploy.
FROM node:22-alpine AS release
WORKDIR /
COPY --from=build /app/build /build
COPY --from=build /app/node_modules /node_modules
COPY --from=build /app/package.json /package.json
ENV NODE_ENV=production
EXPOSE 3000
USER node
CMD ["node", "build"]

Run it capped per lab convention (small web service: 1 CPU / 1G) and put lab-caddy in front for tailnet review lanes.

Behind a reverse proxy

SvelteKit derives the request origin from the host header. Behind lab-caddy (or any proxy), forward the original protocol and host:

PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build

Gotcha: only set these behind a trusted proxy - clients can spoof x-forwarded-* on direct connections. For a fixed canonical origin (needed for CSRF checks on form posts and remote-function calls), set paths.origin in the Vite plugin config instead.

Other adapters

The same pattern applies to adapter-cloudflare (Workers), adapter-static (fully prerendered sites), adapter-vercel, and adapter-netlify; all are wired through the same Vite plugin option in kit 3.