DeployWise
HomeGuidesDeploy SvelteKit to VPS
SvelteKitVPSUbuntuPM2Nginx

Deploy SvelteKit to VPS: Complete Guide

Deploy a production-ready SvelteKit application to any Ubuntu VPS using DeployWise — covers SvelteKit adapters (Node, static, auto), PM2 process management, Nginx reverse proxy, free SSL certificates, and environment variable configuration. No DevOps experience required.

13 min read
Updated 2026

What you need

A VPS running Ubuntu 20.04 or later (DigitalOcean, Hetzner, Vultr, etc.)
SSH access — either password or private key
A GitHub account with your SvelteKit repo
A domain name (optional, but recommended for SSL)

Why deploy SvelteKit to a VPS?

Vercel and similar platforms are excellent for prototypes and small projects. But as your app grows, costs can skyrocket — Vercel charges $0.15/GB for bandwidth above 100 GB, and team plans start at $20/month per member.

A $5/month VPS from Hetzner or DigitalOcean can host multiple production SvelteKit apps with no traffic limits, full SSH access, and complete control over your environment. The tradeoff used to be complexity — but that's what DeployWise solves.

SvelteKit Adapters: Which one to use?

SvelteKit uses adapters to build for different platforms. Your choice determines how your app runs and what you deploy:

adapter-node
npm install -D @sveltejs/adapter-node

Use when: You need SSR (server-side rendering), dynamic routes, or a traditional Node.js app. Most common choice.

Best for: Full SSR support, dynamic content, API routes, middleware — the most flexible option for VPS deployment.

adapter-static
npm install -D @sveltejs/adapter-static

Use when: Your app is content-heavy (blog, docs) or mostly static pages. Builds pre-rendered HTML.

Best for: Ultra-fast load times, works without Node.js, can be served from CDN or cheap static hosting.

adapter-auto
Default in SvelteKit

Use when: You want automatic detection based on environment. DeployWise recommends adapter-node for VPS.

Best for: Auto-detects platform, easy for multi-platform deployments, good for beginners.

Recommendation for VPS: Use @sveltejs/adapter-node for most projects. It provides full SSR, optimal performance on Linux, and is what DeployWise expects for Node.js deployments.

Step 1: Configure your SvelteKit app

Ensure your svelte.config.js is set up for Node.js deployment:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from 'svelte-preprocess/parseAst';

export default {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter({
      // env: {
      //   path: '.env',
      //   prefix: 'CONFIG_'
      // }
    }),
    // Optional: Set your app's base path
    paths: {
      base: process.env.BASE_PATH || '',
    },
  },
};

The adapter-node will compile your SvelteKit app to Node.js during build. The output is stored in the build directory.

1Build your SvelteKit app for production

Compile your SvelteKit app locally to ensure it builds successfully before deploying to your VPS:

npm run build

This will:

1.Compile your Svelte components to JavaScript
2.Bundle and minify your code
3.Optimize assets for production
4.Generate a Node.js-ready output in the build/ directory

Your build output is ready when you see .svelte-kit/build generated. Never commit this directory to git — it's generated during build.

2Environment Variables in SvelteKit

SvelteKit provides different environment variable strategies depending on when they're needed:

$env/static/private

Server-side only, embedded at build time. Use for secrets, API keys, database URLs.

const API_KEY = import.meta.env.VITE_API_KEY; // Available only on server

When to use: Set during build via .env or CI/CD; value baked into compiled app

$env/static/public

Exposed to browser, prefixed with VITE_. Safe to use for non-sensitive config.

const APP_URL = import.meta.env.VITE_APP_URL; // Available in browser + server

When to use: Variables that start with VITE_ are automatically public

$env/dynamic/private

Server-side only, loaded at runtime. Use for runtime-sensitive values.

const SECRET = process.env.SECRET; // Runtime value

When to use: Set in PM2 ecosystem.config.js or systemd env file

$env/dynamic/public

Loaded at runtime, exposed to browser. Less common, use with caution.

Use only for non-sensitive config that may change per deployment

When to use: Rarely needed in practice

DeployWise handles environment variables in the web UI. Set them in project settings before deployment, and they'll be injected at runtime.

3Set up your VPS

Start with a fresh Ubuntu 22.04 LTS droplet/instance. Make sure SSH is accessible on port 22 (or a custom port). You'll need either a root password or an SSH private key.

Recommended minimum specs for a single SvelteKit app:

SpecMinimumRecommended
CPU1 vCPU2 vCPU
RAM512 MB1 GB
Storage20 GB SSD40 GB SSD
Monthly cost~$3/mo~$6/mo

SvelteKit has a minimal footprint and can run on smaller VPS instances compared to Next.js or Nuxt.

4Add your server to DeployWise

Open DeployWise dashboard and sign in with GitHub. Then navigate to Servers → Add Server and fill in:

NameAny label (e.g. "Production VPS")
HostYour server's IP or hostname
Port22 (or your custom SSH port)
Usernameroot or your sudo user
AuthPassword or paste your private key

Click Test Connection. DeployWise will SSH into your server to verify connectivity. If the test passes, save the server.

5Create your SvelteKit project

Go to Projects → New Project. Connect your GitHub account if you haven't already, then configure:

RepositorySelect your SvelteKit GitHub repo
Branchmain or production — whichever you deploy from
Build commandnpm run build (default, can leave blank)
Start commandnode build/index.js — required for Node.js adapter
Port5173 (SvelteKit default) or your custom port
DomainOptional — your custom domain for Nginx + SSL setup
Auto DeployEnable to trigger deploys on every git push
Important: The start command must be node build/index.js for the Node.js adapter. If you're using static generation, use a different command or serve with a static server.

6PM2 Configuration for SvelteKit

DeployWise automatically configures PM2, but here's what it does under the hood. PM2 ensures your SvelteKit app restarts on crashes and boots on server reboot:

module.exports = {
  apps: [
    {
      name: 'sveltekit-app',
      script: './build/index.js',
      exec_mode: 'cluster',
      instances: 2, // Or 'max' for all CPU cores
      env: {
        NODE_ENV: 'production',
        HOST: '0.0.0.0',
        PORT: 5173,
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      merge_logs: true,
      autorestart: true,
      watch: false,
      max_memory_restart: '200M',
    },
  ],
};

Key points: The exec_mode: 'cluster' spreads your app across multiple CPU cores. max_memory_restart prevents memory leaks. DeployWise creates and manages this for you.

7Nginx Reverse Proxy Setup

Nginx sits in front of your SvelteKit app, handling SSL, compression, and static file serving. DeployWise configures this automatically:

upstream sveltekit_app {
  server 127.0.0.1:5173;
}

server {
  listen 80;
  listen [::]:80;
  server_name yourdomain.com;

  # Redirect HTTP to HTTPS
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name yourdomain.com;

  # SSL certificates (Let's Encrypt)
  ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

  # Gzip compression
  gzip on;
  gzip_types text/plain text/css application/javascript;

  location / {
    proxy_pass http://sveltekit_app;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    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;
  }
}

This configuration redirects HTTP to HTTPS, serves static assets efficiently, and proxies dynamic requests to your SvelteKit app on port 5173.

SSL Certificates with Let's Encrypt

DeployWise uses Certbot to issue and auto-renew free SSL certificates from Let's Encrypt. To enable:

1
Point your domain to your VPS

Set an A record (yourdomain.com) and CNAME (www) pointing to your server's IP.

2
Open port 80 (HTTP)

Certbot needs port 80 for domain validation. Allow it: sudo ufw allow 80

3
Configure domain in DeployWise

Enter your domain name when creating the project. DeployWise will request the certificate during deployment.

Certificates auto-renew every 90 days. Certbot runs as a systemd timer on your server.

Common SvelteKit Deployment Mistakes

Wrong SvelteKit adapter

Problem: Using adapter-auto or adapter-static instead of adapter-node for VPS

Solution: Ensure svelte.config.js imports @sveltejs/adapter-node and configures it as the default adapter.

Incorrect start command

Problem: Using npm start or npm run dev instead of node build/index.js

Solution: The start command must point to the compiled Node.js output directly. Use: node build/index.js

Missing build directory

Problem: Deployment fails because .svelte-kit/build is not generated or is in .gitignore

Solution: Never commit build output to git — it's generated during build. DeployWise runs npm run build to create it.

Wrong environment variables scope

Problem: Using $env/static/private variables that need to be dynamic at runtime

Solution: For runtime-configurable values, use $env/dynamic/private or process.env directly in your server code.

Port conflicts

Problem: SvelteKit app tries to use port 5173 but it's already in use

Solution: Check with: lsof -i :5173 — stop conflicting processes or configure a different port in DeployWise.

Memory exhaustion during build

Problem: Build fails with 'FATAL ERROR: JavaScript heap out of memory'

Solution: Increase Node memory: NODE_OPTIONS=--max-old-space-size=1024 npm run build — or add swap to your VPS.

8Deploy your SvelteKit app

Click Deploy Now. DeployWise will execute the following steps on your server automatically:

1.Install Node.js via NVM (if not present)
2.Install PM2 globally
3.Clone your repository via Git
4.Run npm ci to install dependencies
5.Run npm run build to compile SvelteKit
6.Set environment variables from project settings
7.Start/restart the app with PM2 using ecosystem.config.js
8.Configure Nginx reverse proxy
9.Issue Let's Encrypt SSL certificate (if domain configured)

The entire process typically takes 30–60 seconds. You can watch every step live in the deployment log panel.

What happens after deployment

Your SvelteKit app is now:

Running under PM2 in cluster mode — restarts automatically if it crashes
Proxied through Nginx — handles HTTP/HTTPS, gzip compression, and static assets
Protected with SSL — free Let's Encrypt certificate, auto-renewing
Accessible at your domain or via server IP
Pre-configured for auto-restarts on code changes

Future deploys are one click away. If something breaks, roll back to any previous commit from the deployment history.

How DeployWise Automates SvelteKit Deployment

Manually deploying SvelteKit involves 20+ manual steps: SSH setup, dependency installation, build configuration, adapter selection, PM2 ecosystem files, Nginx config generation, SSL certificate management, and environment variable handling. DeployWise does all of this automatically.

Server provisioning
Manual: 30 minDeployWise: Seconds
Adapter + build setup
Manual: 20 minDeployWise: Automatic
PM2 configuration
Manual: 15 minDeployWise: Automatic
Nginx + SSL
Manual: 20 minDeployWise: Automatic
Environment vars
Manual: 10 minDeployWise: Web UI
Monitoring + logs
Manual: Custom scriptsDeployWise: Dashboard

Troubleshooting SvelteKit deployments

Build fails with 'out of memory'

The SvelteKit build is memory-intensive. If your VPS has only 512 MB RAM, add swap: sudo fallocate -l 1G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

App crashes with 'Cannot find module'

Dependencies weren't installed. Ensure npm ci runs before the start command. Check PM2 logs: pm2 logs — verify all packages are in node_modules.

Port 5173 is already in use

Another process is occupying the port. Check: lsof -i :5173 — stop conflicting processes or configure a different port in DeployWise.

Environment variables are undefined at runtime

Check if you're using $env/static/private (build-time only) when you need $env/dynamic/private (runtime). Set vars in DeployWise project settings.

Static files (CSS, JS) return 404

Ensure Nginx is correctly proxying to your SvelteKit app and static file serving is configured. Check Nginx logs: sudo tail -f /var/log/nginx/error.log

SSL certificate fails to issue

Verify domain A record points to your VPS IP, port 80 is open (ufw allow 80), and DNS is propagated (dig yourdomain.com).

Ready to deploy SvelteKit?

Sign in with GitHub, add your VPS, and have your SvelteKit app live in minutes — for free.

Open DeployWise Dashboard

Related guides