
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
You want to run SvelteKit on Kubernetes because your application has outgrown simple PaaS hosting or requires tighter integration with internal microservices. While SvelteKit is often praised for its static site generation, running it as a server-side rendered (SSR) Node.js application in a container orchestrator demands specific configuration to handle hydration, streaming responses, and graceful shutdowns correctly. This guide provides the exact patterns I use in production to deploy resilient SvelteKit workloads, avoiding common pitfalls like missing environment variables during build time or improper signal handling that causes dropped requests during scaling events.
@sveltejs/adapter-node to generate a standalone Node.js server, create a multi-stage Dockerfile to minimize image size, and configure liveness probes against the exposed port (default 3000). Always inject runtime environment variables via ConfigMaps rather than baking them into the image to ensure secure, reproducible deployments across clusters.How do you prepare a SvelteKit application for Kubernetes deployment?
Before writing any YAML, your application must be packaged correctly. SvelteKit defaults to an adapter-based build system, and for Kubernetes, @sveltejs/adapter-node is the standard choice. It outputs a self-contained Node.js application that includes the server handler, prerendered pages, and client assets. A common mistake is trying to run the development server or using adapter-static with a custom Express wrapper; this adds unnecessary complexity and fragility.
Configure the Node Adapter
In your svelte.config.js, ensure the adapter is configured to output to a predictable directory and handle environment variables correctly. For production clusters, disable prerendering for dynamic routes unless you have a specific caching strategy.
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
out: 'build',
precompress: true,
envPrefix: 'PUBLIC_'
})
}
};
export default config; The precompress: true option generates gzip and brotli versions of static assets. When you configure Nginx or an ingress controller later, serving these precompressed files reduces CPU overhead and latency significantly. Understanding resource limits early helps prevent OOM kills during the build phase, which often consumes more memory than the runtime itself.
Create a Production-Grade Multi-Stage Dockerfile
Your Docker image should be lean and secure. Use a multi-stage build to separate dependencies, compilation, and the final runtime. The following Dockerfile is optimized for SvelteKit 2.x and Node.js 20 LTS:
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile --prod=false
# Stage 2: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
ENV NODE_ENV=production
RUN npm run build
# Stage 3: Production Runtime
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 sveltekit
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json ./package.json
COPY --from=deps /app/node_modules ./node_modules
USER sveltekit
EXPOSE 3000
CMD ["node", "build/index.js"] This pattern keeps the final image under 200MB typically. Crucially, we copy only the build directory and production node_modules. Never run containers as root in production; the USER sveltekit directive enforces least privilege, aligning with Kubernetes security best practices.
What Kubernetes manifests are required to run SvelteKit reliably?
With a solid image, you need manifests that respect Node.js behavior. SvelteKit’s Node adapter listens on port 3000 by default and supports graceful shutdown via SIGTERM, but only if configured correctly in your deployment spec.
Deployment with Health Checks
Liveness and readiness probes are non-negotiable. Without them, Kubernetes cannot distinguish between a busy server and a dead one. The Node adapter exposes a basic health endpoint, but you can also probe the root path if your app handles it efficiently.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sveltekit-app
spec:
replicas: 3
selector:
matchLabels:
app: sveltekit-app
template:
metadata:
labels:
app: sveltekit-app
spec:
containers:
- name: sveltekit
image: registry.example.com/sveltekit-app:v1.2.0
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: sveltekit-config
- secretRef:
name: sveltekit-secrets
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5 Note the initialDelaySeconds. Node.js applications, especially those hydrating large component trees on first request, may take several seconds to warm up. Setting this too low causes restart loops. If you encounter issues here, consult my guide on how to debug CrashLoopBackOff effectively.
How should environment variables and secrets be managed for SvelteKit?
SvelteKit distinguishes between public (PUBLIC_*) and private environment variables. In a containerized environment, this distinction becomes critical. Public variables are embedded into the client bundle at build time, while private variables must be available at runtime for server-side operations.
- Build-time variables: Pass
PUBLIC_API_URLduring the Docker build stage. These become immutable in the JavaScript bundle. Changing them requires a new image tag. - Runtime variables: Database credentials, API keys, and session secrets must be injected via Kubernetes Secrets or ConfigMaps. The Node adapter reads
process.envat startup, so these are immediately available without rebuilding. - Never commit secrets: Use external secret managers like AWS Secrets Manager or HashiCorp Vault synced to Kubernetes. For local development,
.envfiles are fine, but never let them touch your CI pipeline.
If you are managing complex configurations across environments, consider reading about Kubernetes secrets management done right to avoid base64-encoded plaintext in your Git repository. Proper secret handling is essential for compliance frameworks like SOC 2 and ISO 27001, especially when handling user data in Nepali fintech or e-commerce applications.
How do you configure Ingress and autoscaling for SvelteKit?
SvelteKit serves both HTML and static assets. Your ingress controller must handle this duality efficiently. While the Node adapter can serve static files, offloading this to Nginx or a CDN improves performance dramatically under load.
| Feature | Node Adapter Only | Ingress + CDN Offload |
|---|---|---|
| Static Asset Serving | Node.js thread (blocking) | Nginx/Cloudflare (non-blocking) |
| Gzip/Brotli Compression | CPU intensive per request | Precompressed or edge-cached |
| SSL Termination | Requires cert-manager in pod | Handled at ingress layer |
| DDoS Protection | Limited to pod resources | WAF rules at edge |
| Cache-Control Headers | Manual header config | Edge cache rules + origin headers |
For autoscaling, SvelteKit is typically CPU-bound during SSR rendering. Configure Horizontal Pod Autoscaler (HPA) based on CPU utilization rather than memory. A target of 70% CPU utilization with a minimum of 2 replicas ensures smooth scaling during traffic spikes. Remember that each pod needs time to warm up; set scaleUp.stabilizationWindowSeconds to at least 60 to prevent flapping.
Run SvelteKit on Kubernetes: Final Checklist
Successfully operating SvelteKit in production requires attention to detail beyond basic deployment. Verify your setup against these criteria before going live:
- Image Security: Scan every build with Trivy or Grype. Ensure no high/critical CVEs exist in the base Node image or dependencies.
- Graceful Shutdown: Test pod termination manually. Send SIGTERM and verify active requests complete before the process exits. The Node adapter handles this, but custom middleware might not.
- Observability: Instrument your app with OpenTelemetry. SvelteKit hooks are ideal places to add tracing spans for server-side rendering and API calls.
- Resource Right-Sizing: Use VPA recommendations after 48 hours of runtime. Over-provisioning wastes money; under-provisioning causes latency.
- Backup Strategy: If your SvelteKit app manages state or uploads, ensure persistent volumes are backed up. Static sites don’t need this, but SSR apps often do.
When you run SvelteKit on Kubernetes with these patterns, you gain scalability without sacrificing developer experience. The framework’s simplicity combined with Kubernetes’ power creates a resilient platform for modern web applications. If your team needs help architecting this stack or auditing an existing deployment, reach out to discuss your infrastructure requirements. I help teams build systems that pass audits and survive peak traffic, whether you’re serving users in Kathmandu or globally.