Self-host your SvelteKit frontend on a VPS using Dokploy. Complete guide to Docker deployment with SSL and automated deployments.

15 min read

Deploying SvelteKit to VPS with Dokploy

Dokploy is an open-source Platform as a Service (PaaS) that simplifies deploying applications on your own VPS. This guide covers deploying SvelteKit to a VPS using Dokploy for complete control over your infrastructure.

Why Self-Host?

  • Cost: Fixed monthly cost vs per-usage pricing
  • Control: Full control over server configuration
  • Privacy: Data stays on your infrastructure
  • Learning: Understand deployment fundamentals
  • No vendor lock-in: Move providers easily

Prerequisites

  • VPS with Docker support (Ubuntu 20.04+ recommended)
  • Domain name pointing to your VPS
  • SvelteKit application ready for deployment
  • SSH access to your server

Install Development Tools Locally

We recommend using mise (version manager) and pnpm (package manager):

Install mise:

curl https://mise.run | sh
echo 'eval "$(mise activate)"' >> ~/.bashrc
source ~/.bashrc

Install Node.js and pnpm:

mise use -g nodejs@latest
mise x nodejs@latest -- npm install -g pnpm

Why pnpm over npm?

  • Faster installation through content-addressable storage
  • Shared package store across projects saves disk space
  • Strict dependency isolation prevents phantom dependencies
  • More reliable lockfile ensures reproducible builds

Step 1: Set Up Your VPS

Server Requirements

  • CPU: 1+ cores
  • RAM: 1GB+ (2GB recommended)
  • Storage: 20GB+ SSD
  • OS: Ubuntu 20.04/22.04 LTS

Initial Server Setup

Connect to your server:

ssh root@your-server-ip

Update system:

apt update && apt upgrade -y

Create a non-root user:

adduser deploy
usermod -aG sudo deploy

Configure SSH (optional but recommended):

# Edit SSH config
nano /etc/ssh/sshd_config

# Disable root login
PermitRootLogin no

# Disable password auth (only if using keys)
PasswordAuthentication no

# Restart SSH
systemctl restart sshd

Install Docker

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

# Add user to docker group
usermod -aG docker deploy

# Verify
systemctl status docker

Step 2: Install Dokploy

# As root or with sudo
curl -sSL https://dokploy.com/install.sh | bash

This installs:

  • Dokploy management interface
  • Traefik reverse proxy
  • Docker networking

Access Dokploy

  1. Open firewall port (if using UFW):
ufw allow 3000/tcp
  1. Access Dokploy dashboard:
http://your-server-ip:3000
  1. Complete initial setup (create admin account)

Step 3: Prepare Your SvelteKit App

Docker Configuration

Create frontend/Dockerfile:

# Build stage
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY pnpm-lock.yaml* ./
COPY yarn.lock* ./

# Install dependencies
RUN if [ -f pnpm-lock.yaml ]; then npm install -g pnpm && pnpm install --frozen-lockfile; \
    elif [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
    else npm ci; fi

# Copy source
COPY . .

# Build
ENV NODE_ENV=production
RUN if [ -f pnpm-lock.yaml ]; then pnpm run build; \
    elif [ -f yarn.lock ]; then yarn build; \
    else npm run build; fi

# Production stage
FROM node:20-alpine

WORKDIR /app

# Copy built app
COPY --from=builder /app/build ./build
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s \
    CMD wget -qO- http://localhost:3000/health || exit 1

# Start
CMD ["node", "build"]

Create frontend/.dockerignore:

node_modules
.pnpm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
.env
.env.*
!.env.production

Update Adapter

Ensure svelte.config.js uses the Node adapter:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter({
      out: 'build'
    })
  }
};

export default config;

Install adapter:

pnpm install -D @sveltejs/adapter-node

Environment Variables

Create frontend/.env.production (committed to repo):

PUBLIC_API_URL=https://tcs-svc-9k2m.yourdomain.com
PUBLIC_GRAPHQL_ENDPOINT=https://tcs-svc-9k2m.yourdomain.com/graphql

Security Note: We use an obscure subdomain (tcs-svc-9k2m) instead of predictable names like api or backend. Attackers constantly scan for common subdomains. An unguessable name hides your API from automated reconnaissance and reduces drive-by attacks. Choose your own unique pattern and keep it private.

Step 4: Configure Dokploy

Add Server to Dokploy

  1. Log into Dokploy dashboard
  2. Go to "Servers"
  3. Click "Add Server"
  4. Enter your server details

Create Application

  1. Go to "Applications"
  2. Click "Create Application"
  3. Configure:

General:

  • Name: frontend
  • Description: SvelteKit frontend

Source:

  • Git Provider: GitHub/GitLab
  • Repository: yourusername/your-repo
  • Branch: main
  • Build Path: frontend

Build:

  • Build Type: Dockerfile
  • Dockerfile Path: Dockerfile

Environment:

  • Port: 3000

Environment Variables

Add in Dokploy UI:

Variable Value
NODE_ENV production
PUBLIC_API_URL https://tcs-svc-9k2m.yourdomain.com

Domain Configuration

  1. In application settings, go to "Domains"
  2. Add domain: tcs-app-7x4p.yourdomain.com
  3. Enable HTTPS (Traefik handles SSL)

Step 5: Deploy

Trigger Deployment

  1. Click "Deploy" in Dokploy UI
  2. Or push to main branch (auto-deploy if configured)

Monitor Deployment

View logs in real-time:

  • Build logs
  • Container logs
  • Error logs

Verify Deployment

# Check container is running
docker ps

# Check logs
docker logs frontend

# Test locally on server
curl http://localhost:3000

Step 6: Configure DNS

Point Domain to Server

At your DNS provider:

Type: A
Name: app
Value: your-server-ip
TTL: 3600

Or for apex domain:

Type: A
Name: @
Value: your-server-ip

SSL Certificate

Dokploy with Traefik automatically handles SSL via Let's Encrypt.

Verify HTTPS works:

curl -I https://tcs-app-7x4p.yourdomain.com

Step 7: Continuous Deployment

Webhook Setup

  1. In Dokploy, get webhook URL:

    • Application → Settings → Webhooks
  2. Add to GitHub/GitLab:

    • Repository → Settings → Webhooks
    • Payload URL: Dokploy webhook URL
    • Content type: application/json
    • Events: Push

Auto-Deploy Configuration

In Dokploy application settings:

  • Enable "Auto Deploy"
  • Select branches to watch

Advanced Configuration

Multiple Instances

Scale horizontally in Dokploy:

  • Application → Settings → Replicas
  • Set to desired count (e.g., 2)

Health Checks

Add to Dockerfile:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1

Environment-Specific Builds

Create separate Dokploy applications:

  • frontend-production
  • frontend-staging

Each with different:

  • Git branches
  • Environment variables
  • Domains

Backup Strategy

SvelteKit frontend is stateless (just files), but backup your:

  • Dokploy configuration
  • Environment variable documentation
  • SSL certificates (Traefik handles automatically)

Troubleshooting

Container Won't Start

Check logs:

docker logs frontend
docker logs traefik

Common issues:

  • Missing environment variables
  • Port already in use
  • Build failure

502 Bad Gateway

Usually means:

  • Container not running
  • Wrong port exposed
  • Health check failing

Check:

docker ps  # Verify container is up
docker inspect frontend  # Check configuration

SSL Not Working

Verify:

  1. Domain DNS points to server
  2. Traefik is running: docker logs traefik
  3. No firewall blocking: ufw status

Build Fails

Test locally first:

docker build -t test-build .

Production Checklist

  • VPS secured (SSH keys, firewall)
  • Dokploy installed and configured
  • Application builds successfully
  • Domain DNS configured
  • HTTPS working with valid certificate
  • Environment variables set
  • Health checks configured
  • Monitoring/logging configured
  • Backup strategy documented
  • Auto-deploy configured (optional)

Cost Comparison

Platform Monthly Cost Notes
Cloudflare Pages Pro $5 Generous free tier, Pro adds more builds
Netlify Pro $19+ Bandage limits
VPS (Hetzner) €5 2GB RAM, 20GB
VPS (DigitalOcean) $6 1GB RAM, 25GB
VPS (Linode) $5 1GB RAM, 25GB

VPS costs are fixed regardless of traffic (within resource limits).

Next Steps