Skip to content

Deploying a SvelteKit App

SvelteKit is a modern meta-framework for Svelte that enables building fast, reactive web applications with excellent performance characteristics. It features file-based routing, server-side rendering (SSR), static site generation (SSG), API routes for backend functionality, form actions for handling submissions, hooks for request processing, and image optimization. SvelteKit compiles Svelte components to highly efficient vanilla JavaScript with minimal bundle sizes, making it ideal for building performant, user-friendly web applications that work offline and load quickly on any device.

This comprehensive guide walks you through deploying a SvelteKit application to Klutch.sh, covering both automatic Nixpacks-based deployments and Docker-based deployments. You’ll learn installation steps, explore sample code, configure adapters, set up environment variables, build API routes, and discover best practices for production deployments.

Table of Contents

  • Prerequisites
  • Getting Started: Create a SvelteKit App
  • Sample Code Examples
  • Project Structure
  • SvelteKit Adapters & Configuration
  • Deploying Without a Dockerfile (Nixpacks)
  • Deploying With a Dockerfile
  • Environment Variables & Configuration
  • Server Routes & API Endpoints
  • Form Actions & Progressive Enhancement
  • Troubleshooting
  • Resources

Prerequisites

To deploy a SvelteKit application on Klutch.sh, ensure you have:

  • Node.js 18 or higher - SvelteKit requires a modern Node.js version
  • npm or yarn - For managing dependencies
  • Git - For version control
  • GitHub account - Klutch.sh integrates with GitHub for continuous deployments
  • Klutch.sh account - Sign up for free

Getting Started: Create a SvelteKit App

Follow these steps to create and set up a new SvelteKit application:

  1. Create a new SvelteKit app using the official create-svelte command:
    Terminal window
    npm create svelte@latest my-sveltekit-app
    cd my-sveltekit-app
    npm install

    The CLI will ask about TypeScript, testing libraries, and other options. Select your preferences.

  2. Start the development server:
    Terminal window
    npm run dev

    Your SvelteKit app will be available at http://localhost:5173. The development server includes hot module reloading for instant feedback.

  3. Explore the generated project structure. SvelteKit creates a `routes` directory under `src` where you can add pages and API endpoints. The framework automatically generates routes based on your file structure.
  4. Build your application for production:
    Terminal window
    npm run build
    npm run preview

    This creates an optimized build and allows you to preview the production build locally.

  5. Stop the development server when ready, press `Ctrl+C`. Verify your app builds successfully before deploying to Klutch.sh.

Sample Code Examples

Basic Page with Svelte Components

Here’s an example of a SvelteKit page with reactive state:

src/routes/+page.svelte
<script>
let count = 0;
let name = '';
function increment() {
count += 1;
}
function handleNameChange(event) {
name = event.target.value;
}
</script>
<svelte:head>
<title>Home - SvelteKit on Klutch.sh</title>
<meta name="description" content="Welcome to SvelteKit" />
</svelte:head>
<div class="container">
<h1>Welcome to SvelteKit</h1>
<p>Deployed on Klutch.sh</p>
<section>
<h2>Counter: {count}</h2>
<button on:click={increment}>Increment</button>
</section>
<section>
<label>
Your name:
<input type="text" value={name} on:change={handleNameChange} />
</label>
{#if name}
<p>Hello, {name}!</p>
{/if}
</section>
</div>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
button {
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
}
</style>

API Route (Server Function)

src/routes/api/posts/+server.ts
import type { RequestHandler } from '@sveltejs/kit';
export const GET: RequestHandler = async () => {
const posts = [
{ id: 1, title: 'First Post', content: 'Hello world' },
{ id: 2, title: 'Second Post', content: 'Another post' }
];
return new Response(JSON.stringify(posts), {
headers: { 'Content-Type': 'application/json' }
});
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
if (!data.title || !data.content) {
return new Response(JSON.stringify({ error: 'Missing fields' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// Save to database here
const newPost = { id: 3, ...data };
return new Response(JSON.stringify(newPost), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
};

Form Actions with Server-Side Handling

src/routes/contact/+page.svelte
<script>
import { enhance } from '$app/forms';
let message = '';
function handleResult({ result }) {
if (result.type === 'success') {
message = 'Message sent successfully!';
} else if (result.type === 'failure') {
message = 'Failed to send message.';
}
}
</script>
<svelte:head>
<title>Contact Us</title>
</svelte:head>
<div class="form-container">
<h1>Contact Us</h1>
{#if message}
<div class="message">{message}</div>
{/if}
<form method="POST" use:enhance={handleResult}>
<input
type="text"
name="name"
placeholder="Your Name"
required
/>
<input
type="email"
name="email"
placeholder="Your Email"
required
/>
<textarea
name="message"
placeholder="Your Message"
required
/>
<button type="submit">Send Message</button>
</form>
</div>
<style>
.form-container {
max-width: 500px;
margin: 0 auto;
padding: 2rem;
}
input, textarea {
width: 100%;
padding: 0.5rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 0.5rem 1rem;
background-color: #0066cc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
src/routes/contact/+page.server.ts
import type { Actions } from '@sveltejs/kit';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const name = data.get('name');
const email = data.get('email');
const message = data.get('message');
if (!name || !email || !message) {
return { success: false };
}
// Process form submission (send email, save to database, etc.)
try {
// Example: send email
await fetch('https://api.example.com/send-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, message })
});
return { success: true };
} catch (error) {
return { success: false };
}
}
};

Project Structure

A typical SvelteKit project has this structure:

my-sveltekit-app/
├── src/
│ ├── routes/
│ │ ├── +layout.svelte
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ │ ├── about/
│ │ │ └── +page.svelte
│ │ ├── posts/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.server.ts
│ │ │ └── [id]/
│ │ │ └── +page.svelte
│ │ ├── api/
│ │ │ └── posts/
│ │ │ └── +server.ts
│ │ └── contact/
│ │ ├── +page.svelte
│ │ └── +page.server.ts
│ ├── lib/
│ │ ├── components/
│ │ └── utils.ts
│ ├── app.html
│ └── app.css
├── static/
│ └── favicon.png
├── .env
├── .env.example
├── svelte.config.js
├── package.json
├── tsconfig.json
└── vite.config.ts

SvelteKit Adapters & Configuration

SvelteKit uses adapters to build for different platforms. For Klutch.sh, use the Node.js adapter:

Install and Configure Node Adapter

Terminal window
npm install --save-dev @sveltejs/adapter-node

Update your svelte.config.js:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
out: 'build',
precompress: false,
envPrefix: ''
})
}
};

Deploying Without a Dockerfile

Klutch.sh uses Nixpacks to automatically detect and build your SvelteKit application. This is the simplest deployment option that requires no additional configuration files.

  1. Test your SvelteKit app locally to ensure it builds and runs correctly:
    Terminal window
    npm run build
    npm run preview
  2. Push your SvelteKit application to a GitHub repository with all source code, configuration files, and lock files included.
  3. Log in to your Klutch.sh dashboard.
  4. Create a new project and give it a name (e.g., "My SvelteKit App").
  5. Create a new app with the following configuration:
    • Repository - Select your SvelteKit GitHub repository and the branch to deploy
    • Traffic Type - Select HTTP (for web applications serving HTTP traffic)
    • Internal Port - Set to 3000 (the default port for SvelteKit applications)
    • Region - Choose your preferred region for deployment
    • Compute - Select appropriate compute resources for your application
    • Instances - Start with 1 instance for testing, scale as needed
    • Environment Variables - Add any required environment variables (API keys, database URLs, etc.)

    If you need to customize the build or start commands, set these Nixpacks environment variables:

    • BUILD_COMMAND: Override the default build command (e.g., npm run build)
    • START_COMMAND: Override the default start command (e.g., node build)
  6. Click "Create" to deploy. Klutch.sh will automatically detect your Node.js/SvelteKit project, install dependencies, build your application, and start it.
  7. Once deployed, your app will be available at a URL like `example-app.klutch.sh`. Test it by visiting the URL in your browser.

Deploying With a Dockerfile

If you prefer more control over the build and runtime environment, you can use a Dockerfile. Klutch.sh will automatically detect and use any Dockerfile in your repository’s root directory.

  1. Create a `Dockerfile` in your project root:
    # Multi-stage build for optimized SvelteKit deployment
    FROM node:18-bullseye-slim AS builder
    WORKDIR /app
    # Copy package files
    COPY package*.json ./
    # Install dependencies
    RUN npm ci
    # Copy source code
    COPY . .
    # Build SvelteKit application
    RUN npm run build
    # Production stage
    FROM node:18-bullseye-slim
    WORKDIR /app
    # Copy package files
    COPY package*.json ./
    # Install production dependencies only
    RUN npm ci --production
    # Copy built application from builder
    COPY --from=builder /app/build ./build
    # Set environment variables
    ENV NODE_ENV=production
    ENV PORT=3000
    # Expose port
    EXPOSE 3000
    # Health check
    HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
    # Start the application
    CMD ["node", "build"]
  2. Create a `.dockerignore` file to exclude unnecessary files from the Docker build:
    node_modules
    npm-debug.log
    .git
    .gitignore
    README.md
    .env
    .env.local
    .vscode
    .idea
    .DS_Store
    build
    .svelte-kit
  3. Push your code (with Dockerfile and .dockerignore) to GitHub.
  4. Follow the same deployment steps as the Nixpacks method:
    • Log in to Klutch.sh
    • Create a new project
    • Create a new app pointing to your GitHub repository
    • Set the traffic type to HTTP and internal port to 3000
    • Add any required environment variables
    • Click “Create”

    Klutch.sh will automatically detect your Dockerfile and use it to build and deploy your application.

  5. Your deployed app will be available at `example-app.klutch.sh` once the build and deployment complete.

Environment Variables & Configuration

SvelteKit applications use environment variables for configuration. Set these in the Klutch.sh dashboard during app creation or update them afterward.

Common Environment Variables

# Server configuration
PORT=3000
NODE_ENV=production
# API configuration
PUBLIC_API_URL=https://api.example.com
VITE_API_URL=https://api.example.com
# Database (if using a backend database)
DATABASE_URL=postgresql://user:password@host:5432/sveltekit_prod
# Authentication
SESSION_SECRET=your_secret_session_key_here
AUTH_TOKEN=your_auth_token
# Third-party services
STRIPE_PUBLIC_KEY=pk_live_xxxxx
STRIPE_SECRET_KEY=sk_live_xxxxx
SENDGRID_API_KEY=SG.xxxxx
# Environment-specific settings
LOG_LEVEL=info
ENABLE_DEBUG=false

Using Environment Variables in SvelteKit

src/lib/config.ts
import { env } from '$env/dynamic/private';
import { PUBLIC_API_URL } from '$env/static/public';
export const getApiUrl = () => {
return PUBLIC_API_URL || 'https://api.example.com';
};
export const getSessionSecret = () => {
const secret = env.SESSION_SECRET;
if (!secret) {
throw new Error('SESSION_SECRET environment variable is not set');
}
return secret;
};
export const isDevelopment = () => {
return process.env.NODE_ENV === 'development';
};
export const isProduction = () => {
return process.env.NODE_ENV === 'production';
};
<!-- Use public variables in components -->
<script>
import { PUBLIC_API_URL } from '$env/static/public';
</script>
<p>API URL: {PUBLIC_API_URL}</p>

Server Routes & API Endpoints

SvelteKit makes it easy to build API endpoints and server-side functionality:

Creating API Routes

src/routes/api/users/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from '@sveltejs/kit';
export const GET: RequestHandler = async () => {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
return json(users);
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
if (!data.name || !data.email) {
return json(
{ error: 'Name and email are required' },
{ status: 400 }
);
}
// Save user to database
const newUser = { id: 3, ...data };
return json(newUser, { status: 201 });
};

Using Endpoints in Components

src/routes/users/+page.svelte
<script>
import { onMount } from 'svelte';
let users = [];
let loading = true;
let error = null;
onMount(async () => {
try {
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch users');
users = await response.json();
} catch (err) {
error = err.message;
} finally {
loading = false;
}
});
</script>
<svelte:head>
<title>Users</title>
</svelte:head>
{#if loading}
<p>Loading...</p>
{:else if error}
<p>Error: {error}</p>
{:else}
<ul>
{#each users as user (user.id)}
<li>{user.name} - {user.email}</li>
{/each}
</ul>
{/if}

Form Actions & Progressive Enhancement

SvelteKit’s form actions provide server-side handling with progressive enhancement:

src/routes/login/+page.server.ts
import type { Actions } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
export const actions: Actions = {
login: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
// Validate input
if (!email || !password) {
return { success: false, message: 'Email and password required' };
}
// Authenticate user
try {
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
return { success: false, message: 'Invalid credentials' };
}
const { token } = await response.json();
// Set session cookie
cookies.set('session', token, {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 7 // 7 days
});
throw redirect(302, '/dashboard');
} catch (error) {
return { success: false, message: 'Login failed' };
}
}
};

Troubleshooting

Build Failures

Problem - Deployment fails during the build phase

Solution:

  • Verify your SvelteKit app builds locally: npm run build
  • Ensure all dependencies are properly listed in package.json
  • Check for TypeScript errors: npm run check (if configured)
  • Verify that the adapter is installed and configured
  • Check build logs in the Klutch.sh dashboard for specific errors
  • Ensure svelte.config.js is correctly configured with the Node adapter

Application Won’t Start

Problem - App shows as unhealthy after deployment

Solution:

  • Verify the app starts locally: npm run preview
  • Check that port 3000 is configured correctly in Klutch.sh
  • Verify all required environment variables are set
  • Check application logs in the Klutch.sh dashboard
  • Ensure the built application is being deployed (check build directory)
  • Verify that the build directory was created during build process

Memory Issues

Problem - App crashes with out of memory errors

Solution:

  • Upgrade to a larger compute tier in Klutch.sh
  • Optimize component rendering (avoid expensive computations)
  • Implement lazy loading for large datasets
  • Monitor memory usage during development
  • Check for memory leaks in server routes
  • Consider implementing caching strategies

Slow Builds

Problem - Build takes longer than expected

Solution:

  • Use the multi-stage Dockerfile to reduce final image size
  • Cache dependencies properly in Docker
  • Optimize imports and tree-shake unused code
  • Check for large dependencies that can be replaced
  • Use npm ci instead of npm install for faster, reproducible builds
  • Enable SvelteKit’s precompression during build

Performance Issues

Problem - App runs slowly in production

Solution:

  • Enable compression for HTTP responses
  • Use CDN for static assets
  • Implement proper caching headers
  • Optimize images and assets
  • Profile performance with browser DevTools
  • Use SvelteKit’s built-in optimization features
  • Consider implementing service workers for offline functionality

Resources


Summary

Deploying a SvelteKit application on Klutch.sh is straightforward whether you choose Nixpacks or Docker. SvelteKit’s modern architecture with server-side rendering, API routes, form actions, and progressive enhancement makes it ideal for building fast, interactive web applications. Both deployment methods provide reliable, scalable hosting for your SvelteKit apps. Start with Nixpacks for simplicity, or use Docker for complete control over your build environment. With SvelteKit’s excellent developer experience and Klutch.sh’s scalable infrastructure, you can deploy production-ready applications that deliver exceptional performance and user experience at scale.