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.
What you need
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:
npm install -D @sveltejs/adapter-nodeUse 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.
npm install -D @sveltejs/adapter-staticUse 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.
Default in SvelteKitUse 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:
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:
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 serverWhen to use: Set during build via .env or CI/CD; value baked into compiled app
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 + serverWhen to use: Variables that start with VITE_ are automatically public
Server-side only, loaded at runtime. Use for runtime-sensitive values.
const SECRET = process.env.SECRET; // Runtime valueWhen to use: Set in PM2 ecosystem.config.js or systemd env file
Loaded at runtime, exposed to browser. Less common, use with caution.
Use only for non-sensitive config that may change per deploymentWhen 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:
| Spec | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 512 MB | 1 GB |
| Storage | 20 GB SSD | 40 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:
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:
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:
Set an A record (yourdomain.com) and CNAME (www) pointing to your server's IP.
Certbot needs port 80 for domain validation. Allow it: sudo ufw allow 80
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
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.
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
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.
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.
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.
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:
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:
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.
Troubleshooting SvelteKit deployments
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
Dependencies weren't installed. Ensure npm ci runs before the start command. Check PM2 logs: pm2 logs — verify all packages are in node_modules.
Another process is occupying the port. Check: lsof -i :5173 — stop conflicting processes or configure a different port in DeployWise.
Check if you're using $env/static/private (build-time only) when you need $env/dynamic/private (runtime). Set vars in DeployWise project settings.
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
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