# SvelteKit Deployment Guide ## Overview SvelteKit supports multiple deployment targets through adapters. Choose the right adapter for your infrastructure and requirements. --- ## 🔌 Available Adapters ### Adapter Comparison | Adapter | Platform | SSR | SSG | Edge | Serverless | |---------|----------|-----|-----|------|------------| | `adapter-node` | Node.js | ✅ | ✅ | ❌ | ❌ | | `adapter-auto` | Auto-detect | ✅ | ✅ | Varies | Varies | | `adapter-vercel` | Vercel | ✅ | ✅ | ✅ | ✅ | | `adapter-netlify` | Netlify | ✅ | ✅ | ✅ | ✅ | | `adapter-cloudflare` | Cloudflare | ✅ | ✅ | ✅ | ✅ | | `adapter-static` | Static hosts | ❌ | ✅ | ❌ | ❌ | --- ## 🖥️ Node.js Deployment (adapter-node) ### Installation ```bash npm install -D @sveltejs/adapter-node ``` ### Configuration ```javascript // svelte.config.js 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({ // Output directory out: 'build', // Precompress static assets with gzip/brotli precompress: true, // Environment variable configuration envPrefix: 'APP_' }) } }; export default config; ``` ### Build & Run ```bash # Build npm run build # Run with Node node build/index.js # Or with environment variables PORT=3000 HOST=0.0.0.0 node build/index.js ``` ### Environment Variables ```bash # Server configuration PORT=3000 # Default: 3000 HOST=0.0.0.0 # Default: 0.0.0.0 ORIGIN=https://example.com # Required for form actions BODY_SIZE_LIMIT=512K # Request body limit ``` ### Production Process Manager (PM2) ```javascript // ecosystem.config.cjs module.exports = { apps: [{ name: 'sveltekit-app', script: './build/index.js', instances: 'max', exec_mode: 'cluster', env: { NODE_ENV: 'production', PORT: 3000, ORIGIN: 'https://example.com' }, error_file: './logs/error.log', out_file: './logs/output.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z' }] }; ``` ```bash # Start with PM2 pm2 start ecosystem.config.cjs # Save process list pm2 save # Setup startup script pm2 startup ``` ### Systemd Service ```ini # /etc/systemd/system/sveltekit-app.service [Unit] Description=SvelteKit Application After=network.target [Service] Type=simple User=www-data WorkingDirectory=/var/www/app ExecStart=/usr/bin/node build/index.js Restart=always RestartSec=10 Environment=NODE_ENV=production Environment=PORT=3000 Environment=ORIGIN=https://example.com [Install] WantedBy=multi-user.target ``` ```bash sudo systemctl enable sveltekit-app sudo systemctl start sveltekit-app ``` ### Nginx Reverse Proxy ```nginx # /etc/nginx/sites-available/sveltekit-app upstream sveltekit { server 127.0.0.1:3000; keepalive 64; } server { listen 80; server_name example.com; # Redirect to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name example.com; # SSL certificates ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL settings ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Gzip gzip on; gzip_types text/plain text/css application/json application/javascript; # Static files with immutable cache location /_app/immutable { proxy_pass http://sveltekit; add_header Cache-Control "public, max-age=31536000, immutable"; } # Other static files location /static { proxy_pass http://sveltekit; add_header Cache-Control "public, max-age=86400"; } # Proxy to SvelteKit location / { proxy_pass http://sveltekit; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } } ``` --- ## 🐳 Docker Deployment ### Dockerfile ```dockerfile # Build stage FROM node:20-alpine AS builder WORKDIR /app # Copy package files COPY package*.json ./ # Install dependencies RUN npm ci # Copy source COPY . . # Build application RUN npm run build # Production stage FROM node:20-alpine WORKDIR /app # Copy built application COPY --from=builder /app/build ./build COPY --from=builder /app/package*.json ./ # Install production dependencies only RUN npm ci --omit=dev # Set environment ENV NODE_ENV=production ENV PORT=3000 # Expose port EXPOSE 3000 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 # Run CMD ["node", "build/index.js"] ``` ### docker-compose.yml ```yaml version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - ORIGIN=https://example.com - DATABASE_URL=postgresql://user:pass@db:5432/app depends_on: - db - redis restart: unless-stopped healthcheck: test: ["CMD", "wget", "--spider", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: app restart: unless-stopped redis: image: redis:7-alpine volumes: - redis_data:/data restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./certs:/etc/nginx/certs:ro depends_on: - app restart: unless-stopped volumes: postgres_data: redis_data: ``` --- ## 📦 Static Site Generation (adapter-static) ### Installation ```bash npm install -D @sveltejs/adapter-static ``` ### Configuration ```javascript // svelte.config.js import adapter from '@sveltejs/adapter-static'; const config = { kit: { adapter: adapter({ pages: 'build', assets: 'build', fallback: '404.html', // or 'index.html' for SPA precompress: true, strict: true }), prerender: { entries: ['*'], // Prerender all pages handleHttpError: 'warn' } } }; export default config; ``` ### Page Configuration for SSG ```typescript // src/routes/+layout.ts export const prerender = true; // Prerender all pages export const ssr = true; // Enable SSR during build // src/routes/+page.ts export const prerender = true; // Prerender this specific page // For dynamic routes, export entries export function entries() { return [ { slug: 'post-1' }, { slug: 'post-2' }, { slug: 'post-3' } ]; } ``` ### Deploy to GitHub Pages ```yaml # .github/workflows/deploy.yml name: Deploy to GitHub Pages on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Build run: npm run build env: BASE_PATH: '/${{ github.event.repository.name }}' - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./build ``` --- ## ☁️ Vercel Deployment ### Configuration ```javascript // svelte.config.js import adapter from '@sveltejs/adapter-vercel'; const config = { kit: { adapter: adapter({ runtime: 'nodejs20.x', // or 'edge' regions: ['iad1'], // Deploy regions split: true // Split into multiple functions }) } }; ``` ### Per-Route Configuration ```typescript // src/routes/api/slow-endpoint/+server.ts export const config = { runtime: 'nodejs20.x', maxDuration: 30 // Extend timeout }; // src/routes/api/fast-endpoint/+server.ts export const config = { runtime: 'edge', regions: ['iad1', 'sfo1', 'cdg1'] // Multi-region }; ``` ### vercel.json ```json { "framework": "sveltekit", "buildCommand": "npm run build", "installCommand": "npm ci", "headers": [ { "source": "/_app/immutable/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ] } ``` --- ## 🌐 Cloudflare Pages ### Configuration ```javascript // svelte.config.js import adapter from '@sveltejs/adapter-cloudflare'; const config = { kit: { adapter: adapter({ routes: { include: ['/*'], exclude: [''] } }) } }; ``` ### Platform Bindings ```typescript // src/app.d.ts declare global { namespace App { interface Platform { env: { KV_NAMESPACE: KVNamespace; D1_DATABASE: D1Database; R2_BUCKET: R2Bucket; }; context: { waitUntil(promise: Promise): void; }; } } } // src/routes/+page.server.ts export async function load({ platform }) { const value = await platform?.env.KV_NAMESPACE.get('key'); return { value }; } ``` --- ## 🔒 Security Considerations ### Environment Variables ```typescript // src/lib/server/env.ts import { env } from '$env/dynamic/private'; import { building } from '$app/environment'; if (!building) { // Validate required env vars at startup const required = ['DATABASE_URL', 'SECRET_KEY']; for (const key of required) { if (!env[key]) { throw new Error(`Missing required environment variable: ${key}`); } } } export const config = { databaseUrl: env.DATABASE_URL, secretKey: env.SECRET_KEY, isProduction: env.NODE_ENV === 'production' }; ``` ### Security Headers ```typescript // src/hooks.server.ts import type { Handle } from '@sveltejs/kit'; export const handle: Handle = async ({ event, resolve }) => { const response = await resolve(event); // Security headers response.headers.set('X-Frame-Options', 'SAMEORIGIN'); response.headers.set('X-Content-Type-Options', 'nosniff'); response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); response.headers.set( 'Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" ); response.headers.set( 'Permissions-Policy', 'camera=(), microphone=(), geolocation=()' ); return response; }; ``` ### CORS Configuration ```typescript // src/routes/api/[...path]/+server.ts import type { RequestHandler } from './$types'; const allowedOrigins = ['https://example.com', 'https://app.example.com']; export const OPTIONS: RequestHandler = async ({ request }) => { const origin = request.headers.get('origin'); if (origin && allowedOrigins.includes(origin)) { return new Response(null, { headers: { 'Access-Control-Allow-Origin': origin, 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400' } }); } return new Response(null, { status: 403 }); }; ``` --- ## 📊 Health Checks & Monitoring ### Health Endpoint ```typescript // src/routes/health/+server.ts import { json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; export const GET: RequestHandler = async () => { const health = { status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), version: process.env.npm_package_version }; // Add database check try { // await db.query('SELECT 1'); health.database = 'connected'; } catch { health.database = 'disconnected'; health.status = 'degraded'; } const statusCode = health.status === 'healthy' ? 200 : 503; return json(health, { status: statusCode }); }; ``` --- ## 📋 Deployment Checklist ### Pre-Deployment - [ ] All tests passing - [ ] Environment variables documented - [ ] Build succeeds locally - [ ] Security headers configured - [ ] Error pages customized - [ ] Logging configured ### Production - [ ] SSL/TLS enabled - [ ] CDN configured - [ ] Database backups scheduled - [ ] Monitoring/alerting setup - [ ] Rate limiting enabled - [ ] Health checks configured ### Post-Deployment - [ ] Smoke tests passing - [ ] Performance metrics acceptable - [ ] Error tracking working - [ ] Rollback plan documented