Prerequisites
- A RamNode VPS with Ubuntu 24.04 LTS installed (1 GB RAM minimum, 2 GB recommended)
- SSH access to your VPS as root or a sudo-enabled user
- A domain name pointed to your VPS IP address (A record configured in DNS)
- A SvelteKit project ready to deploy (using the Node adapter)
- Basic familiarity with the Linux command line
💡 Tip: For most SvelteKit applications, RamNode's 2 GB RAM plan provides more than enough headroom for Node.js, Nginx, and your application. Scale up only when your traffic demands it.
Initial Server Setup
ssh root@your-server-ip
apt update && apt upgrade -yCreate a Deploy User
Running applications as root is a security risk. Create a dedicated deploy user:
adduser deploy
usermod -aG sudo deploy
su - deployConfigure the Firewall
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusInstall Node.js
SvelteKit requires Node.js 18.13 or later. Node.js 22 LTS provides excellent performance improvements for SSR workloads and long-term support through April 2027.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
# Verify the installation
node --version # Should output v22.x.x
npm --version # Should output 10.x.xPrepare Your SvelteKit Project
Install the Node Adapter
SvelteKit uses adapters to target different deployment environments. For a VPS deployment with SSR, you need the Node adapter:
npm install -D @sveltejs/adapter-nodeConfigure svelte.config.js
import adapter from '@sveltejs/adapter-node';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
// Output directory for the build
out: 'build',
// Precompress assets with gzip and brotli
precompress: true,
// Environment variable prefix
envPrefix: 'APP_'
})
}
};
export default config;Add Environment Variables
Create a .env file in your project root (do NOT commit to version control):
APP_HOST=127.0.0.1
APP_PORT=3000
NODE_ENV=production
ORIGIN=https://yourdomain.comDeploy to Your VPS
Option A: Deploy with Git (Recommended)
# On your VPS as the deploy user
cd /home/deploy
git clone https://github.com/yourusername/your-sveltekit-app.git app
cd app
# Install dependencies and build
npm ci --omit=dev
npm run buildOption B: Upload a Pre-Built Bundle
# On your local machine
npm run build
# Transfer the build output to your VPS
scp -r build package.json package-lock.json deploy@your-server-ip:~/app/
# On the VPS, install production dependencies
cd ~/app && npm ci --omit=devTest the Build
cd ~/app
PORT=3000 HOST=127.0.0.1 node build/index.js
# Press Ctrl+C once confirmedPM2 Process Management
PM2 keeps your SvelteKit app running continuously, restarts it on crashes, and manages log rotation.
sudo npm install -g pm2Create Ecosystem File
module.exports = {
apps: [{
name: 'sveltekit-app',
script: 'build/index.js',
cwd: '/home/deploy/app',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
HOST: '127.0.0.1',
ORIGIN: 'https://yourdomain.com'
},
max_memory_restart: '300M',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
error_file: '/home/deploy/logs/sveltekit-error.log',
out_file: '/home/deploy/logs/sveltekit-out.log'
}]
};Cluster mode with instances: 'max' spawns one process per CPU core. On RamNode's 2-core VPS plans, this gives you two worker processes for better request handling.
# Create the logs directory
mkdir -p ~/logs
# Start the app with PM2
cd ~/app
pm2 start ecosystem.config.cjs
# Verify it's running
pm2 status
pm2 logs sveltekit-app --lines 20
# Save the process list and enable startup on boot
pm2 save
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u deploy --hp /home/deployConfigure Nginx Reverse Proxy
Nginx handles SSL termination, static asset caching, and HTTP/2 connections in front of your Node.js application.
sudo apt install -y nginxserver {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
# Redirect all HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL certificates (added by Certbot in Step 8)
# ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json
application/javascript text/xml application/xml
image/svg+xml;
# Proxy to SvelteKit
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
# Cache immutable static assets aggressively
location /immutable/ {
proxy_pass http://127.0.0.1:3000;
expires 1y;
add_header Cache-Control "public, immutable";
}
}sudo ln -s /etc/nginx/sites-available/sveltekit /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxSSL with Let's Encrypt
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Verify auto-renewal is configured
sudo certbot renew --dry-runCertbot automatically installs a systemd timer that checks for certificate renewal twice daily. No cron job is needed.
Automated Deployments
#!/bin/bash
set -e
APP_DIR=/home/deploy/app
echo "[$(date)] Starting deployment..."
cd $APP_DIR
git pull origin main
npm ci --omit=dev
npm run build
pm2 reload ecosystem.config.cjs
echo "[$(date)] Deployment complete!"chmod +x ~/deploy.sh
# Run whenever you push changes
./deploy.shFor automated CI/CD, trigger this script via a GitHub Actions workflow using SSH, or set up a webhook endpoint that calls it on push events.
Security Hardening
1. Disable root SSH login
Edit /etc/ssh/sshd_config and set PermitRootLogin no, then restart the SSH service.
2. Set up SSH key authentication
Copy your public key with ssh-copy-id deploy@your-server-ip, then disable password authentication in sshd_config.
3. Install Fail2Ban
Run sudo apt install -y fail2ban to automatically block repeated failed login attempts.
4. Enable automatic security updates
Run sudo apt install -y unattended-upgrades and configure it to apply security patches automatically.
5. Set up log rotation
PM2 handles its own log rotation with pm2 install pm2-logrotate, keeping your disk space in check.
Performance Tuning
Node.js Memory Settings
For a 2 GB RAM VPS, set appropriate memory limits to prevent out-of-memory issues:
NODE_OPTIONS: '--max-old-space-size=512'Nginx Tuning
worker_processes auto;
worker_connections 1024;
# Enable sendfile for static assets
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Keepalive settings
keepalive_timeout 65;
keepalive_requests 100;SvelteKit Precompression
With precompress: true in your adapter config, SvelteKit generates .gz and .br files at build time. Nginx can serve these directly:
location /immutable/ {
proxy_pass http://127.0.0.1:3000;
gzip_static on;
brotli_static on; # Requires nginx-mod-brotli
expires 1y;
add_header Cache-Control "public, immutable";
}Troubleshooting
| Issue | Solution |
|---|---|
| 502 Bad Gateway | Verify PM2 is running with pm2 status. Check that port 3000 matches in both ecosystem config and Nginx proxy_pass. |
| App crashes on start | Check pm2 logs sveltekit-app. Common causes: missing ORIGIN env variable, incorrect build path, or missing dependencies. |
| CORS errors | Set the ORIGIN environment variable to your exact domain URL including protocol (https://yourdomain.com). |
| SSL certificate issues | Run sudo certbot renew --force-renewal. Check that DNS A records point to your VPS IP. |
| High memory usage | Reduce PM2 cluster instances or lower max-old-space-size. Monitor with pm2 monit. |
| Slow builds | Ensure your VPS has at least 2 GB RAM. SvelteKit's Vite bundler benefits from available memory during compilation. |
Quick Reference Commands
| Command | Description |
|---|---|
pm2 status | View running processes |
pm2 logs sveltekit-app | Tail application logs |
pm2 reload ecosystem.config.cjs | Zero-downtime restart |
pm2 monit | Real-time resource monitoring |
sudo nginx -t | Test Nginx configuration |
sudo systemctl reload nginx | Apply Nginx changes |
sudo certbot renew | Renew SSL certificates |
sudo ufw status | Check firewall rules |
SvelteKit Application Deployed Successfully!
Your SvelteKit application is now running in production on a RamNode VPS with PM2 process management, Nginx reverse proxy with HTTP/2, SSL encryption, and an automated deployment workflow. Consider adding a CDN like Cloudflare for edge caching and explore RamNode's higher-tier plans as your traffic grows.
