
SvelteKit mit Docker bereitstellen
Jonas ScholzDu willst deine SvelteKit-App als Node-Server in Docker starten? Dafür nehmen wir adapter-node, bauen die App und kopieren die Ausgabe in eine eigene Runtime-Stufe. Für eine rein statische Svelte-Seite brauchst du ein anderes Setup.
adapter-node einrichten
Noch kein Projekt? Erstell eins mit npx sv create my-svelte-app, wähle npm und installier die Pakete. Füg dann im Projektverzeichnis den Adapter hinzu:
npm install -D @sveltejs/adapter-node
Ändere den Adapter-Import in svelte.config.js von @sveltejs/adapter-auto zu @sveltejs/adapter-node. Lass deine anderen Einstellungen und Preprocessors stehen. So sieht die Config für den Adapter aus:
import adapter from "@sveltejs/adapter-node";
export default {
kit: {
adapter: adapter(),
},
};
Der Adapter erzeugt standardmäßig einen Node-Server im Verzeichnis build. Weitere Optionen findest du in der Dokumentation zu adapter-node.
Build und Runtime trennen
FROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:24-bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=3000
COPY --from=build --chown=node:node /app/build ./build
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "build"]
Die erste Stufe installiert die Pakete aus der Lockdatei und baut die App. Danach entfernt sie die Entwicklungsabhängigkeiten. Die letzte Stufe bekommt den Server, die übrigen Pakete und package.json. Mit node build startet der fertige Server.
Pakete, die dein Server zur Laufzeit lädt, gehören in dependencies. Build-Tools kommen in devDependencies. Schieb also nicht alle Pakete nach devDependencies, um Platz zu sparen. Was nicht im Bundle steckt und zur Laufzeit gebraucht wird, muss im Runtime-Image installiert sein.
Leg eine .dockerignore an. Damit bleiben lokale Build-Dateien und Zugangsdaten aus dem Build-Kontext:
node_modules
.svelte-kit
build
.git
.env
.env.*
npm-debug.log
Bauen und starten
docker build -t sveltekit-app .
docker run --rm --name sveltekit-app -p 3000:3000 -e ORIGIN=http://localhost:3000 sveltekit-app
Öffne http://localhost:3000 und test auch ein Formular. Mit ORIGIN sagst du SvelteKit, unter welcher öffentlichen Adresse die App läuft. Setz den Wert in Produktion auf deinen HTTPS-Origin, zum Beispiel https://app.example.com. Lass den Pfad weg.
Du kannst den Origin auch über weitergeleitete Header ermitteln lassen. Vertrau dabei nur den Headern deines Reverse-Proxys. Bei einer festen öffentlichen Domain ist es einfacher, ORIGIN direkt zu setzen.
Wann liest SvelteKit die Umgebungsvariablen?
Das hängt davon ab, welches Modul du nutzt:
$env/dynamic/private: Liest Werte zur Laufzeit. Du kannst sie nur im Server-Code nutzen.$env/dynamic/public: Liest Werte zur Laufzeit. Werte mit öffentlichem Präfix sind auch im Browser-Code verfügbar.$env/static/private: Schreibt Werte beim Build fest. Du kannst sie nur im Server-Code nutzen.$env/static/public: Schreibt Werte beim Build fest. Werte mit öffentlichem Präfix sind auch im Browser-Code verfügbar.
Das öffentliche Präfix ist standardmäßig PUBLIC_. Pack dort keine Zugangsdaten rein. Statische Werte stehen schon nach dem Build fest. Eine andere Umgebungsvariable beim Containerstart ändert sie nicht mehr.
Gib Runtime-Werte mit docker run -e oder über die Umgebungsvariablen deines Hosters mit. Verlass dich nicht darauf, dass der Produktionsserver deine lokale .env lädt.
Einen Health-Endpunkt ergänzen
Leg src/routes/health/+server.ts an:
export function GET() {
return new Response("ok");
}
Füg diese Anweisung in die Runtime-Stufe des Dockerfiles ein:
HEALTHCHECK --interval=30s --timeout=3s CMD node -e "fetch('http://127.0.0.1:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
Damit prüfst du, ob der HTTP-Server antwortet. Die Datenbank und andere Dienste prüft der Endpunkt noch nicht. Braucht dein Deployment solche Readiness-Checks, ergänze sie gezielt.
RAM-Verbrauch und Deployment
NODE_OPTIONS=--max-old-space-size=512 begrenzt den Old-Generation-Heap von V8. Der gesamte Prozess kann trotzdem mehr RAM brauchen. Native Module, Buffer und andere Speicherbereiche zählen zusätzlich zum Container-Limit. Plane dafür Platz ein und miss den Verbrauch. Das Heap-Limit allein verhindert keine Abstürze.
Hast du Docker Scout installiert, prüf das Image mit docker scout quickview sveltekit-app. Bau es neu, wenn du Pakete oder das Basis-Image aktualisieren musst.
Für das Deployment auf Sliplane committe das Dockerfile und wähle dein Repository aus. Setz den HTTP-Port auf 3000 und hinterlege den öffentlichen Service-Origin als ORIGIN. Test deine Formulare noch einmal, wenn du eine eigene Domain hinzufügst.