svelte-kit-docker-skeleton Svelte Themes

Svelte Kit Docker Skeleton

Just seeing if claude can build me a good skeleton

SvelteKit Skeleton

Production-ready, fully containerised boilerplate for SvelteKit applications.

Layer Technology
Runtime & package manager Bun
Framework SvelteKit + Svelte 5
Database PostgreSQL 18
ORM Drizzle
Auth Better Auth + admin plugin
Styling Tailwind CSS v4 (OKLCH theming)
Unit tests Vitest
E2E tests Playwright
Local dev VS Code DevContainers
Reverse proxy Caddy (auto-HTTPS)
CI/CD GitHub Actions → SSH deploy

Project structure

.
├── .devcontainer/          # VS Code DevContainer config (dev only)
├── .github/workflows/      # GitHub Actions CI/CD pipeline
├── drizzle/                # Generated SQL migrations (committed)
├── src/
│   ├── app.css             # Tailwind v4 CSS-first config + OKLCH theme
│   ├── app.html            # HTML shell (anti-FOUC theme script lives here)
│   ├── hooks.server.ts     # Auth session injection + Better Auth route handler
│   ├── lib/
│   │   ├── auth-client.ts  # Client-side Better Auth instance
│   │   └── server/
│   │       ├── auth.ts     # Server-side Better Auth instance
│   │       ├── db.ts       # Drizzle ORM client
│   │       ├── schema.ts   # PostgreSQL schema (incl. Better Auth tables)
│   │       └── seed.ts     # Admin user seed script
│   └── routes/             # SvelteKit file-based router
├── tests/
│   ├── e2e/                # Playwright end-to-end tests
│   └── unit/               # Vitest unit tests
├── Caddyfile               # Caddy reverse-proxy + auto-HTTPS config
├── docker-compose.yml      # Production stack (app + db + caddy)
├── Dockerfile              # Multi-stage Bun build
├── drizzle.config.ts
├── playwright.config.ts
└── vite.config.ts          # Vite + Vitest config

Prerequisites

Local machine (without DevContainer)

  • Bun ≥ 1.2
  • Docker + Docker Compose v2
  • Node.js ≥ 20 (for Playwright browser downloads only)

DevContainer path


# 1. Clone the repository
git clone https://github.com/your-org/svelte-skeleton.git
cd svelte-skeleton

# 2. Copy environment file
cp .env.example .env
# Edit .env — at minimum set BETTER_AUTH_SECRET to a random string:
openssl rand -base64 32

# 3. Open in VS Code
code .
# When prompted: "Reopen in Container" — VS Code will build the dev containers,
# install dependencies, push the schema, and seed the admin user automatically.
# (postCreateCommand in devcontainer.json)

# 4. Start the dev server inside the container terminal
bun run dev
# → http://localhost:5173

Manual setup (without DevContainer)

# 1. Clone and enter the project
git clone https://github.com/your-org/svelte-skeleton.git
cd svelte-skeleton

# 2. Copy and edit environment variables
cp .env.example .env

# 3. Start a local PostgreSQL instance (Docker)
docker run -d \
  --name svelte-skeleton-db \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=devpassword \
  -e POSTGRES_DB=appdb \
  -p 5432:5432 \
  postgres:18-alpine

# 4. Install dependencies
bun install

# 5. Push schema to the database (creates all tables)
bun run db:push

# 6. Seed the admin user
bun run db:seed

# 7. Start the development server
bun run dev
# → http://localhost:5173

Environment variables

Copy .env.example to .env and fill in the values:

Variable Required Description
DATABASE_URL Yes Full PostgreSQL connection string
BETTER_AUTH_SECRET Yes ≥32-char random secret. Generate: openssl rand -base64 32
BETTER_AUTH_URL Yes Canonical URL of the app (e.g. https://example.com)
DOMAIN Prod only Public domain — Caddy provisions TLS for this
CADDY_EMAIL Prod only Email for Let's Encrypt expiry notifications
POSTGRES_USER Prod only PostgreSQL username for docker-compose
POSTGRES_PASSWORD Prod only PostgreSQL password
POSTGRES_DB Prod only Database name
ADMIN_EMAIL Seed only Initial admin account email
ADMIN_PASSWORD Seed only Initial admin account password

Database management

# Apply schema changes without generating migration files (dev only)
bun run db:push

# Generate a SQL migration file from schema changes (recommended for production)
bun run db:generate

# Apply pending migrations to the database
bun run db:migrate

# Open Drizzle Studio (visual DB browser at http://localhost:4983)
bun run db:studio

# Seed the initial admin user
bun run db:seed

Testing

# Run unit tests (Vitest)
bun run test

# Run unit tests in watch mode
bun run test:watch

# Install Playwright browsers (first time only)
bunx playwright install --with-deps

# Run end-to-end tests (Playwright)
# Automatically builds + previews the app before running
bun run test:e2e

# Run e2e tests with browser visible (headed mode)
bunx playwright test --headed

Git workflow

# Always branch from main
git checkout main
git pull origin main
git checkout -b feat/your-feature-name

# Make changes, then run tests locally before pushing
bun run check       # TypeScript + Svelte type check
bun run test        # Unit tests
bun run test:e2e    # End-to-end tests (optional locally)

# Stage and commit
git add src/ tests/
git commit -m "feat: add your feature description"

# Push and open a PR
git push -u origin feat/your-feature-name
# Open a PR on GitHub → main

# After PR approval and merge, the GitHub Actions deploy workflow
# fires automatically and deploys to production via SSH.

# Clean up local branch
git checkout main
git pull origin main
git branch -d feat/your-feature-name

VPS initial setup

Run these commands once on a fresh Ubuntu/Debian VPS.

1. Generate an SSH key pair for CI/CD (on your local machine)

# Generate a dedicated deploy key (ED25519 is fast and secure)
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key

# Print the PUBLIC key — add this to the VPS authorized_keys
cat ~/.ssh/deploy_key.pub

# Print the PRIVATE key — add this as the VPS_SSH_KEY GitHub Secret
cat ~/.ssh/deploy_key

2. Harden the VPS and install Docker

# SSH into your VPS as root (replace with your VPS IP)
ssh root@YOUR_VPS_IP

# Create a deploy user with limited privileges
adduser deploy
usermod -aG sudo deploy
usermod -aG docker deploy   # Allow docker commands without sudo (after Docker install)

# Add the deploy SSH key
mkdir -p /home/deploy/.ssh
echo "PASTE_PUBLIC_KEY_HERE" >> /home/deploy/.ssh/authorized_keys
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

# Disable password authentication (key-only SSH)
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl reload sshd

# Install Docker (official script)
curl -fsSL https://get.docker.com | sh

# Enable Docker on boot
systemctl enable docker
systemctl start docker

# Logout and log back in as deploy user to apply group changes
exit
ssh deploy@YOUR_VPS_IP

3. Clone the repository and configure production

# SSH in as the deploy user
ssh deploy@YOUR_VPS_IP

# Create the app directory
sudo mkdir -p /opt/app
sudo chown deploy:deploy /opt/app

# Clone your repository
git clone https://github.com/your-org/svelte-skeleton.git /opt/app
cd /opt/app

# Create and populate the production .env file
cp .env.example .env
nano .env
# Fill in ALL required production values:
#   DOMAIN, CADDY_EMAIL, POSTGRES_USER, POSTGRES_PASSWORD,
#   POSTGRES_DB, BETTER_AUTH_SECRET, BETTER_AUTH_URL

# First-time build and start
docker compose up -d --build

# Wait for the DB to be ready, then run migrations and seed
sleep 10
docker compose exec app bun run db:migrate
docker compose exec app bun run db:seed

# Verify all services are running
docker compose ps

4. Add GitHub Actions secrets

In your GitHub repository → Settings → Secrets and variables → Actions, add:

Secret Value
VPS_HOST Your VPS IP address or domain
VPS_USER deploy
VPS_SSH_KEY Contents of ~/.ssh/deploy_key (the private key)
VPS_PORT 22 (optional, only if non-standard)

From this point, every push to main triggers an automatic deployment.


Theming

The colour system is defined in src/app.css using OKLCH. To change the colour palette, edit the CSS custom properties in :root:

:root {
  --hue-primary: 272;      /* 0–360: violet */
  --chroma-primary: 0.20;  /* 0–0.4: saturation */

  --hue-secondary: 195;
  --chroma-secondary: 0.15;

  --hue-neutral: 260;
  --chroma-neutral: 0.015;
}

The @theme inline block in app.css generates the full Tailwind utility scale (bg-primary-500, text-secondary-200, etc.) from these variables at runtime — meaning dark mode is a pure CSS operation with zero JavaScript re-computation.


Production operations

# View live logs
docker compose logs -f app

# Restart only the app (e.g. after .env changes)
docker compose restart app

# Pull the latest code and rebuild manually
git pull origin main && docker compose up -d --build

# Run a one-off database migration on the VPS
docker compose exec app bun run db:migrate

# Open a database REPL
docker compose exec db psql -U postgres appdb

# Check Caddy TLS certificate status
docker compose exec caddy caddy list-modules

License

MIT

Top categories

Loading Svelte Themes