Ship production SvelteKit.
Not toy apps.
Second edition. An opinionated course on building a multi-tenant SaaS in SvelteKit 2, shipping it on a VPS you actually run, and not lying to yourself about any of it. Svelte 5, Drizzle, Better Auth, Stripe Connect.
- Format
- Cohort + self-paced · video + written
- Length
- 23 modules · ~110 lessons · video + written
- Stack
- Svelte 5 · SvelteKit 2 · Postgres
- App
- Multi-tenant invoicing SaaS
Most SvelteKit material stops at the demo. You finish the tutorial, you build the todo app, and the moment you try to ship something with paying users you are alone with decisions no blog post covers.
This course is the opposite of that. It comes out of running real SvelteKit apps in production, at one hundred and eight hundred thousand users a month, and writing down the decisions that mattered. The ones nobody told you about. The ones you only learned by losing a weekend.
No filler. No ten-minute lessons that say what the docs already say. Every module earns its place by saving you a problem that is going to find you anyway.
You'll be able to do four things you can't do today
- 01
Ship a multi-tenant SaaS, not a tutorial app
You'll build a freelancer invoicing platform from empty repo to paying users. Workspaces, recurring invoices, PDFs, Stripe Connect payouts. The kind of app you can actually put a price tag on.
- 02
Own the data layer with Drizzle and Postgres
Drizzle schemas that still make sense to you in six months. Migrations you can run in production without flinching. Multi-tenant scoping that doesn't leak rows between workspaces because someone forgot a where clause.
- 03
Wire up auth, payments, payouts, and email properly
Better Auth wired the way you'd actually want it. Stripe Connect for payouts to seller accounts. Webhooks that handle retries the way Stripe expects. Email that lands in inboxes, jobs that run on time.
- 04
Operate it on a VPS — not just deploy it
Provision an Ubuntu box, run the app behind Caddy, deploy without downtime. Backups you've actually restored from at least once. Logs and PostHog events wired up so you find out about a bug before your users do.
The exact tools, with reasons
No mystery dependencies. Every choice below is the one I'd make today if I were starting a new SvelteKit SaaS from scratch.
- 01 Svelte 5 Component runtime · runes Reactivity that survives an app, not just a demo
- 02 SvelteKit 2 Application framework One model that covers server, browser, and edge
- 03 TypeScript The language No mystery types six months from now
- 04 PostgreSQL Primary database Self-hosted on a VPS. Alternatives covered
- 05 Drizzle ORM Schema · queries · migrations SQL you can read, types you can trust
- 06 Better Auth Authentication Email + OAuth, sessions over JWTs
- 07 Tailwind 4 Styles No bespoke design system to maintain
- 08 shadcn-svelte UI primitives Copy-in components you actually own
- 09 Valibot Validation Schemas shared across server and client
- 10 pg-boss Background jobs A queue that lives in your database
- 11 Resend Transactional email Templates as components, not strings
- 12 Stripe Connect Payments and payouts Payment intents on connected accounts
- 13 Docker · Caddy · VPS Deployment Ubuntu, docker-compose, Lets Encrypt
Twenty-three modules, roughly one hundred and ten lessons, every one of them earning its place
Listed below in full. Video-first per lesson, with a condensed written companion per module so you can revisit without scrubbing video. If a lesson doesn't save you a problem you'd otherwise hit, it doesn't exist in the course.
- 00 4 lessons
Course intro
Quick orientation. What we'll build, what you should already know, where the repo lives, how the lesson companions work.
- 0.1 What we will build, end to end
- 0.2 Prerequisites and stance
- 0.3 The repo, branches, and lesson companions
- 0.4 How this course is different
- 01 5 lessons
Svelte 5 fundamentals
Runes from first principles. Snippets and {@render}. Attachments. The .svelte.ts files and class-based state that quietly change how you structure a Svelte app.
- 1.1 $state, $derived, $effect — the core three
- 1.2 $derived.by, $effect.pre, $state.raw, $state.snapshot
- 1.3 Snippets, {@render}, and replacing slots
- 1.4 {@attach} and events as attributes
- 1.5 .svelte.ts files, classes as state, $props.id()
- 02 5 lessons
SvelteKit 2 fundamentals
Routing, layouts, loads, hooks. The boundary between server and browser code. The mental model you need before you commit to a project structure.
- 2.1 Routing, layouts, and route groups
- 2.2 Loads — universal vs server, params, parent()
- 2.3 Parallel loads, invalidation, streaming promises
- 2.4 +server.ts, hooks, and request handlers
- 2.5 $lib/server, $env/static vs $env/dynamic
- 03 4 lessons
Project bootstrap
A project skeleton that scales. TypeScript, Tailwind 4, shadcn-svelte. The naming and folder conventions that keep a two-hundred-route app navigable a year in.
- 3.1 TypeScript config that scales
- 3.2 Tailwind 4 + shadcn-svelte setup
- 3.3 ESLint, Prettier, and conventions that compound
- 3.4 Base layout, navigation, page options
- 04 5 lessons
Database — Postgres + Drizzle
Schemas you can read out loud. Migrations you can run in production without crossing your fingers. A self-hosted Postgres on a VPS that you actually control, with backups that work.
- 4.1 Schema design — cuid2, timestamps, relations
- 4.2 Self-hosted Postgres on a VPS — docker-compose and backups
- 4.3 Migrations: generate, review, ship, recover
- 4.4 Per-table organization and transactions
- 4.5 Sidebar — Supabase, Neon, PlanetScale tradeoffs
- 05 5 lessons
Forms & data mutations
Form actions, use:enhance, and fail() as the foundation. Then remote functions (form, command, query, prerender) and when each one replaces the older pattern.
- 5.1 Form actions, use:enhance, fail() — the foundation
- 5.2 Remote functions — form, command, query, prerender
- 5.3 When remote functions replace older patterns
- 5.4 Valibot schemas shared across client and server
- 5.5 Optimistic UI via derived overriding
- 06 5 lessons
Authentication — Better Auth
Email and password done correctly. OAuth without a third-party SDK. Sessions in cookies. Auth checks placed where they will actually run, not where they get silently skipped on a client-side navigation.
- 6.1 Better Auth setup — sessions, hooks, getRequestEvent()
- 6.2 Email + password — signup, verify, reset
- 6.3 OAuth — Google and GitHub, properly
- 6.4 Where auth checks live (and why not in layouts)
- 6.5 Account settings, email change, deletion
- 07 5 lessons
Multi-tenant data model
Workspaces, membership, invitations. Scoping patterns that hold up under refactor. Defense in depth, so a missed where clause never leaks rows between tenants.
- 7.1 Workspaces, membership, invitations
- 7.2 Workspace switcher and state
- 7.3 Scoping every query — patterns that hold up
- 7.4 Row-level safety as defense in depth
- 7.5 Inviting, rotating, and revoking access
- 08 5 lessons
Building the app — clients & invoices
The product itself. List views with search and pagination. Detail panes via shallow routing. A streaming dashboard. Totals that survive currencies, tax, and rounding.
- 8.1 Modeling clients, invoices, line items, statuses
- 8.2 List views — search, filter, pagination
- 8.3 Detail panes via shallow routing
- 8.4 A dashboard with streaming loads
- 8.5 Computing totals — currency, tax, rounding
- 09 4 lessons
PDF generation
Server-side PDF generation that renders consistently. For the invoice itself, for receipts, for anything else that has to leave the system as a document.
- 9.1 Server-side PDF generation strategy
- 9.2 Templating an invoice PDF
- 9.3 Storage and download endpoints
- 9.4 Consistent rendering across locales
- 10 4 lessons
File uploads
Local dev to S3-compatible storage. Signed URLs. Image resizing on upload. Attaching files to records without leaking access.
- 10.1 Local dev — filesystem and signed URLs
- 10.2 S3-compatible storage — R2, Tigris, MinIO
- 10.3 Image resizing on upload
- 10.4 Attaching files to records
- 11 4 lessons
Email — Resend
Transactional email treated as a subsystem of its own. Templates as components. Deliverability that lands inboxes. The unsubscribe flow you need before launch day.
- 11.1 Templates and the dev preview
- 11.2 Transactional sends — invoices, receipts, reminders
- 11.3 Deliverability — DKIM, SPF, DMARC, warm-up
- 11.4 Unsubscribe and compliance
- 12 5 lessons
Background jobs — pg-boss
A queue that lives in your Postgres. Recurring invoices, payment reminders, retries, dead-letter handling. A basic ops dashboard so you can sleep at night.
- 12.1 Why a queue lives in your database
- 12.2 Recurring invoices — scheduling and drift
- 12.3 Payment reminders and digest emails
- 12.4 Retries and dead-letter handling
- 12.5 A basic ops dashboard for jobs
- 13 5 lessons
Stripe Connect
Onboarding connected accounts. Payment intents. Payment links per invoice. Webhooks that survive Stripe's retry policy. A test-mode workflow you can trust before you flip to live.
- 13.1 Onboarding — Standard vs Express
- 13.2 Payment intents on connected accounts
- 13.3 Payment links per invoice
- 13.4 Webhooks — signature verification and idempotency
- 13.5 Refunds, payouts, and the test-mode workflow
- 14 4 lessons
Real-time
SSE for invoice status. Reconciling SSE state with SvelteKit's invalidate(). A clear-eyed look at when WebSockets and Durable Objects start paying off.
- 14.1 SSE for invoice status — sent, viewed, paid
- 14.2 Reconciling SSE with invalidate()
- 14.3 When to reach for WebSockets
- 14.4 When to reach for Durable Objects
- 15 5 lessons
Admin dashboard
A separate /admin route group with role gating. The metrics that matter (MRR, GMV, connected-account health). The manual interventions every product eventually needs.
- 15.1 The /admin route group and role gating
- 15.2 MRR, GMV, connected-account health
- 15.3 User search and impersonation
- 15.4 Manual interventions — refunds, comping plans
- 15.5 Audit log — what to log and how
- 16 4 lessons
Errors, loading, edge cases
error() vs handleError(). +error.svelte boundaries that help instead of hiding the bug. Transport hooks for Date and BigInt. The Stripe webhook failure modes that will eventually find you.
- 16.1 error() vs handleError() — the right tool
- 16.2 +error.svelte boundaries that help
- 16.3 Transport hooks — Date, BigInt, custom types
- 16.4 Stripe webhook failure modes you must handle
- 17 4 lessons
Testing
Unit tests where they pay, skipped where they don't. Integration tests against a real Postgres. Playwright running through Stripe test mode. Tests that don't break the moment you refactor.
- 17.1 Vitest unit tests that pay
- 17.2 Integration tests against a real Postgres
- 17.3 Playwright — auth flows and Stripe test mode
- 17.4 Test factories and CI
- 18 4 lessons
Performance & SEO
Image optimization. Prerendering marketing pages. JSON-LD, sitemaps, meta tags. A Lighthouse pass that doesn't lie to you.
- 18.1 Image optimization in SvelteKit
- 18.2 Prerendering marketing pages
- 18.3 JSON-LD, sitemap, meta — the works
- 18.4 The Lighthouse pass that matters
- 19 5 lessons
Observability
Structured logs you can grep. PostHog wired up for the events that matter (HTTP requests, user events, errors). Typed event wrappers instead of ad-hoc capture calls. Request IDs that correlate browser to server.
- 19.1 Structured logs — what to log and what to skip
- 19.2 PostHog on the client — init, identify, reverse-proxy
- 19.3 Server-side PostHog — typed event wrappers in hooks
- 19.4 Errors via PostHog captureException
- 19.5 Request IDs and correlating browser → server
- 20 6 lessons
Deployment — VPS
Ubuntu provisioning, Docker, Caddy with Let's Encrypt. Zero-downtime deploys. Migrations that run on deploy. Healthchecks. A backup-and-restore drill you've already done once, on a Sunday, before you needed it.
- 20.1 Provisioning Ubuntu — SSH and firewall hardening
- 20.2 Docker + docker-compose for app and Postgres
- 20.3 Caddy with Lets Encrypt
- 20.4 Migrations on deploy and zero-downtime
- 20.5 Healthchecks and rolling back fast
- 20.6 Sidebar — same app on Vercel and Cloudflare
- 21 9 lessons
Working with coding agents
Setting your SvelteKit project up so coding agents are productive on it instead of producing slop you have to clean up. AGENTS.md and CLAUDE.md. The framework gotchas to encode. Post-edit contracts the agent has to run. Knowing when an agent earns its keep, and when it costs more than it saves.
- 21.1 The case for agent-native SvelteKit development
- 21.2 AGENTS.md and CLAUDE.md — what to write, what to leave out
- 21.3 Encoding the gotchas the framework won't catch
- 21.4 Per-folder AGENTS.md for narrowing context
- 21.5 Pointing agents at the right docs (Svelte and SvelteKit llms.txt)
- 21.6 Post-edit contracts — format, lint, check as commands the agent runs
- 21.7 Plan mode and reviewing approach before code lands
- 21.8 Subagents and skills for repeatable workflows
- 21.9 Knowing when to skip the agent
- 22 5 lessons
Appendices
The migrations and tradeoffs you'll eventually face. Svelte 4 to 5. Lucia to Better Auth. Prisma to Drizzle. When Postgres should become D1 or Turso.
- 22.1 Svelte 4 → 5 migration
- 22.2 Lucia → Better Auth migration
- 22.3 Prisma → Drizzle migration
- 22.4 When Postgres should become D1 or Turso
- 22.5 BullMQ vs pg-boss revisited
A freelancer invoicing SaaS, end to end
One app, threaded through every module, finished by the end. Not three half-built demos. By module twenty-one it's multi-tenant, takes payments via Stripe Connect, chases late payers automatically, and runs on a VPS you provisioned by hand.
- 01 Workspaces with membership and invitations
- 02 Clients, invoices, line items, recurring invoices
- 03 Server-side PDF generation and download endpoints
- 04 Stripe Connect onboarding and payouts to seller accounts
- 05 Webhooks with signature verification and idempotency
- 06 Real-time invoice status (sent · viewed · paid) over SSE
- 07 Background jobs for reminders and recurring billing
- 08 Admin dashboard — MRR, GMV, audit log, manual interventions
- 09 Self-hosted on a VPS — Docker, Caddy, zero-downtime deploys
Recent writing on building with SvelteKit
- I got a $134 Cloudflare D1 bill. Here's how I cut it 95% Database
- Why I set out to create the best SvelteKit course Opinion
- Svelte slots, and how to migrate to snippets Opinion
- Add Google authentication to SvelteKit with Better Auth Authentication
- SvelteKit authentication: email and password with Better Auth Authentication
One tier. Everything included. No upsells.
- All twenty-three modules · roughly one hundred and ten lessons · video + written companion
- Source code for every project, every commit, every branch
- Cohort access (twelve weeks of office hours and a private channel)
- Self-paced access after the cohort closes. Same material, same channel.
- Lifetime updates. When the framework moves, the course moves with it.
- Free upgrade to all future major versions, the same way v1 owners got v2
- Fourteen-day no-questions refund
Need a team license, an invoice, or a country-specific PPP discount? Reach out before checkout.
Things people ask before reserving a seat
- Q.01 I bought v1. What do I do?
- Nothing. Your access is being upgraded to v2 automatically and you'll get an email with the new course link as soon as the cohort opens. If it doesn't show up by launch day, write in and we'll sort it the same day.
- Q.02 Who is this for?
- Developers comfortable in JavaScript or TypeScript who want to ship a real, paying SvelteKit app (auth, billing, payouts, deploys) and stop guessing at every architectural decision. If you've built side projects but never one that survived its first hundred users, you are the audience.
- Q.03 Do I need to know Svelte 5 already?
- No. Module 1 grounds Svelte 5 from a real-app perspective (runes, snippets, attachments) and module 2 does the same for SvelteKit 2. If you've used React, Vue, or Solid in production, you'll keep up.
- Q.04 What about the framework changing?
- The course is built against Svelte 5 and SvelteKit 2 and tracks the framework as it evolves. When something material shifts (a new adapter, a runtime change, a Svelte version), modules are revised and existing students get the update at no cost.
- Q.05 Is there a cohort, or self-paced?
- Both. The first cohort runs over twelve weeks with weekly office hours and a private channel. After it closes the same material is available self-paced, and the channel stays open.
- Q.06 What if I need a refund?
- Fourteen days, no questions. After that, the material is yours.
- Q.07 Do you offer a team license?
- Yes. Five and ten-seat tiers with invoice billing and a private cohort channel for your team. Reach out before checkout and we'll set it up.
If you're tired of tutorials that stop right before the part that matters, this is the course I wish someone had handed me.