kaboot/docs/PRODUCTION.md

13 KiB

Production Deployment Guide

This guide provides instructions for deploying Kaboot to a production environment with Caddy reverse proxy and automatic HTTPS.

Quick Start (Automated Setup)

The easiest way to deploy Kaboot is using the automated setup script:

# Run the production setup script with your domains
./scripts/setup-prod.sh --domain kaboot.example.com --auth-domain auth.example.com

# Or run interactively (will prompt for domains)
./scripts/setup-prod.sh

This script automatically:

  • Generates all required secrets (database passwords, API tokens, etc.)
  • Creates the production .env file
  • Creates the Caddyfile with your domains
  • Creates the Authentik production blueprint
  • Removes the development blueprint
  • Builds the frontend with production URLs

After running the script, start the stack:

docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d

Accessing Authentik Admin

After the stack is running, you can access the Authentik admin interface:

  1. URL: https://auth.your-domain.com/if/admin/
  2. Username: akadmin
  3. Password: The password shown when you ran setup-prod.sh (stored as AUTHENTIK_BOOTSTRAP_PASSWORD in .env)

From the admin interface you can:

  • Manage users and groups
  • View application access logs
  • Configure additional OAuth providers
  • Set up email for password recovery
  • Customize the login/signup UI

Manual Setup

If you prefer to configure manually, follow these steps:

# 1. Generate secrets
./scripts/setup.sh

# 2. Configure production blueprint
cp authentik/blueprints/kaboot-setup-production.yaml.example \
   authentik/blueprints/kaboot-setup-production.yaml

# Edit the blueprint - update domains in the 'context' section:
#   kaboot_domain: your-app.com
#   auth_domain: auth.your-app.com

# Remove dev blueprint to avoid conflicts
rm authentik/blueprints/kaboot-setup.yaml

# 3. Configure Caddyfile
cp Caddyfile.example Caddyfile
# Edit Caddyfile - replace example.com with your domains

# 4. Update .env with production values
#    - OIDC_ISSUER=https://auth.your-app.com/application/o/kaboot/
#    - OIDC_JWKS_URI=https://auth.your-app.com/application/o/kaboot/jwks/
#    - CORS_ORIGIN=https://your-app.com

# 5. Build frontend with production URLs
VITE_API_URL=https://your-app.com/api \
VITE_OIDC_AUTHORITY=https://auth.your-app.com/application/o/kaboot/ \
npm run build

# 6. Start the stack
docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d

# 7. Verify
docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml ps

Prerequisites

  • A Linux server with Docker and Docker Compose installed.
  • A registered domain name (e.g., example.com).
  • DNS A records pointing your domains to your server (see below).
  • A Google Gemini API key (optional, for AI quiz generation).

DNS Configuration

You need two DNS records pointing to your server's IP address. You can use either:

  • Subdomains: kaboot.example.com + auth.example.com (recommended)
  • Apex + subdomain: example.com + auth.example.com

Finding Your Server's IP

# On your server, run:
curl -4 ifconfig.me

Namecheap Setup

  1. Log in to Namecheap and go to Domain List
  2. Click Manage next to your domain
  3. Go to the Advanced DNS tab
  4. Add the following A Records:
Type Host Value TTL
A Record kaboot YOUR_SERVER_IP Automatic
A Record auth YOUR_SERVER_IP Automatic

Note

: The "Host" field is the subdomain prefix only. For kaboot.example.com, enter just kaboot.

Cloudflare Setup

  1. Log in to Cloudflare and select your domain
  2. Go to DNSRecords
  3. Click Add record and create:
Type Name IPv4 address Proxy status
A kaboot YOUR_SERVER_IP DNS only (gray cloud)
A auth YOUR_SERVER_IP DNS only (gray cloud)

Important: Set proxy status to "DNS only" (gray cloud) for both records. Caddy handles SSL, and Cloudflare's proxy can interfere with certificate generation.

Other Providers (GoDaddy, Google Domains, etc.)

The process is similar for most providers:

  1. Find the DNS Management or DNS Settings section
  2. Add two A Records:
    • Name/Host: kabootValue/Points to: YOUR_SERVER_IP
    • Name/Host: authValue/Points to: YOUR_SERVER_IP
  3. Set TTL to automatic or 300 seconds

Verify DNS Propagation

After adding records, verify they're working (may take 5-30 minutes):

# Check if DNS resolves to your server
dig kaboot.example.com +short
dig auth.example.com +short

# Or use nslookup
nslookup kaboot.example.com
nslookup auth.example.com

Both should return your server's IP address. You can also use dnschecker.org to verify global propagation.

Architecture Overview

In production, the stack consists of:

  • Caddy: Reverse proxy with automatic HTTPS via Let's Encrypt.
  • Authentik: Identity Provider for OAuth2/OIDC authentication.
  • Kaboot Backend: Express server for quiz logic and SQLite storage.
  • Kaboot Frontend: Static assets served via Caddy.
  • PostgreSQL: Database for Authentik.
  • Redis: Cache and task queue for Authentik.

Environment Variables

The setup-prod.sh script generates the .env file automatically. If you need to create it manually, here's the required configuration:

Backend & Authentik Configuration

# Database (generate with: openssl rand -base64 36)
PG_PASS=your_strong_postgres_password
PG_USER=authentik
PG_DB=authentik

# Authentik Secrets (generate with: openssl rand -base64 60)
AUTHENTIK_SECRET_KEY=your_strong_authentik_secret

# Bootstrap credentials - used for initial Authentik setup
# AUTHENTIK_BOOTSTRAP_PASSWORD: Admin password (generate with: openssl rand -base64 24)
# AUTHENTIK_BOOTSTRAP_TOKEN: API token (generate with: openssl rand -hex 32)
AUTHENTIK_BOOTSTRAP_PASSWORD=your_admin_password
AUTHENTIK_BOOTSTRAP_TOKEN=your_api_token

# OIDC Production Settings (update with your domains)
OIDC_ISSUER=https://auth.example.com/application/o/kaboot/
OIDC_JWKS_URI=https://auth.example.com/application/o/kaboot/jwks/

# Security
CORS_ORIGIN=https://kaboot.example.com
NODE_ENV=production
LOG_REQUESTS=true

Frontend Configuration

The frontend requires environment variables at build time (not runtime):

  • VITE_API_URL: https://kaboot.example.com/api
  • VITE_OIDC_AUTHORITY: https://auth.example.com/application/o/kaboot/

The setup-prod.sh script sets these automatically when building.

Docker Compose Files

The project includes pre-configured compose files:

  • docker-compose.prod.yml - Production services (Authentik, Backend, PostgreSQL, Redis)
  • docker-compose.caddy.yml - Caddy reverse proxy overlay

These files are ready to use - no need to create your own.

HTTPS and Reverse Proxy

Choose one of the following reverse proxy options. Caddy is recommended for its simplicity and automatic HTTPS.

Caddy automatically obtains and renews SSL certificates from Let's Encrypt.

A separate docker-compose.caddy.yml is provided to add Caddy to your stack.

Step 1: Create the Caddyfile

Copy and customize the example Caddyfile:

cp Caddyfile.example Caddyfile

Edit Caddyfile and replace kaboot.example.com and auth.example.com with your actual domains:

kaboot.example.com {
    root * /srv/frontend
    file_server
    try_files {path} /index.html

    handle /api/* {
        reverse_proxy kaboot-backend:3001
    }

    handle /health {
        reverse_proxy kaboot-backend:3001
    }
}

auth.example.com {
    reverse_proxy authentik-server:9000 {
        header_up X-Forwarded-Proto {scheme}
        header_up X-Forwarded-Host {host}
        transport http {
            keepalive 30s
        }
    }
}

Step 2: Build the Frontend

npm run build

This creates the dist/ directory with production assets.

Step 3: Start with Caddy

Use both compose files together:

docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d

This will:

  • Start all Kaboot services (backend, Authentik, PostgreSQL, Redis)
  • Start Caddy as a reverse proxy on ports 80 and 443
  • Automatically obtain SSL certificates from Let's Encrypt

Step 4: Verify

Check that all services are running:

docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml ps

View Caddy logs:

docker logs kaboot-caddy

Check Authentik blueprint was applied:

docker compose -f docker-compose.prod.yml logs authentik-server | grep -i blueprint

Stopping the Stack

docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml down

Option 2: Nginx

Use Nginx as a reverse proxy with manual SSL certificate management.

server {
    listen 443 ssl;
    server_name kaboot.example.com;

    ssl_certificate /etc/letsencrypt/live/kaboot.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/kaboot.example.com/privkey.pem;

    location / {
        root /var/www/kaboot/frontend;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://localhost:3001/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /health {
        proxy_pass http://localhost:3001/health;
    }
}

server {
    listen 443 ssl;
    server_name auth.example.com;

    ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Authentik Configuration for Production

The setup-prod.sh script handles all Authentik configuration automatically:

./scripts/setup-prod.sh --domain your-app.com --auth-domain auth.your-app.com

This creates the production blueprint with your domains and removes the development blueprint.

Option B: Manual Blueprint Setup

If configuring manually:

# 1. Copy and configure the production blueprint
cp authentik/blueprints/kaboot-setup-production.yaml.example \
   authentik/blueprints/kaboot-setup-production.yaml

# 2. Edit the blueprint and update the domains
#    Find the 'context' section and change:
#    - kaboot_domain: kaboot.example.com  -> your-app.com
#    - auth_domain: auth.example.com      -> auth.your-app.com

# 3. Remove the development blueprint (prevents conflicts)
rm authentik/blueprints/kaboot-setup.yaml

# 4. Start the stack - blueprint applies automatically
docker compose -f docker-compose.prod.yml up -d

Option C: Manual UI Configuration

  1. Update Redirect URIs: In the Authentik Admin interface, go to Applications > Providers > Kaboot OAuth2. Update the Redirect URIs to use your production domain:
    • https://kaboot.example.com/callback
    • https://kaboot.example.com/silent-renew.html
    • https://kaboot.example.com

Email Configuration

To enable password recovery, configure SMTP settings in Authentik. In the Admin interface, go to System > Settings and update the Email section. - Host: your SMTP server - Port: 587 or 465 - Username/Password: your credentials - Use TLS/SSL: Enabled

Database Backup Strategy

Kaboot uses SQLite, making backups straightforward.

SQLite (Kaboot Data)

The database file is located in the kaboot-data volume at /data/kaboot.db. To back it up:

docker exec kaboot-backend sqlite3 /data/kaboot.db ".backup '/data/backup_$(date +%F).db'"

Then, copy the backup file from the volume to a secure location.

PostgreSQL (Authentik Data)

For Authentik's metadata:

docker exec kaboot-postgresql pg_dump -U authentik authentik > authentik_backup_$(date +%F).sql

Security Checklist

  • Run ./scripts/setup-prod.sh to generate strong secrets (don't use defaults).
  • Ensure NODE_ENV is set to production.
  • Use HTTPS for all connections (Caddy handles this automatically).
  • Set CORS_ORIGIN to your specific frontend domain (e.g., https://kaboot.example.com).
  • Update OIDC_ISSUER and OIDC_JWKS_URI to use your auth domain with HTTPS.
  • Regularly back up the kaboot.db and PostgreSQL data.
  • Monitor logs by setting LOG_REQUESTS=true.
  • Keep Docker images updated to the latest stable versions.
  • Configure firewall to only allow ports 80 and 443 from the internet.