kaboot/docs/PRODUCTION.md

14 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 with domain configuration
  • Creates the Caddyfile with your domains
  • Creates the Authentik production blueprint
  • Removes the development blueprint

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
#    - KABOOT_DOMAIN=your-app.com
#    - AUTH_DOMAIN=auth.your-app.com
#    - 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. Start the stack (frontend builds automatically in Docker)
docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d --build

# 6. 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).

Note

: Node.js is not required on the production server. The frontend is built inside a Docker container.

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:

  • Kaboot Frontend: Multi-stage Docker build (Node.js builds assets, Caddy serves them). Includes automatic HTTPS via Let's Encrypt.
  • Kaboot Backend: Express server for quiz logic and SQLite storage.
  • Authentik: Identity Provider for OAuth2/OIDC authentication.
  • 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 is built inside Docker using the KABOOT_DOMAIN and AUTH_DOMAIN variables from your .env file. These are passed as build arguments to the Dockerfile:

  • VITE_API_URL: https://${KABOOT_DOMAIN}
  • VITE_BACKEND_URL: https://${KABOOT_DOMAIN}
  • VITE_AUTHENTIK_URL: https://${AUTH_DOMAIN}

The setup-prod.sh script sets these domain variables automatically.

Stripe Payments Configuration (Optional)

To enable paid AI access subscriptions, configure Stripe:

# Stripe API Keys (get from https://dashboard.stripe.com/apikeys)
STRIPE_SECRET_KEY=sk_live_...        # Use sk_test_... for testing
STRIPE_WEBHOOK_SECRET=whsec_...      # From webhook endpoint configuration
STRIPE_PRICE_ID_MONTHLY=price_...    # Monthly subscription price ID
STRIPE_PRICE_ID_YEARLY=price_...     # Yearly subscription price ID

Stripe Dashboard Setup

  1. Create a Product in Stripe Dashboard:

    • Name: "Kaboot AI Pro"
    • Description: "250 AI quiz generations per month"
  2. Add Pricing:

    • Monthly: $5.00/month (recurring)
    • Yearly: $50.00/year (recurring)
    • Copy the Price IDs (start with price_)
  3. Configure Webhook:

    • Go to Developers > Webhooks
    • Add endpoint: https://your-domain.com/api/payments/webhook
    • Select events:
      • checkout.session.completed
      • customer.subscription.updated
      • customer.subscription.deleted
      • invoice.paid
      • invoice.payment_failed
      • refund.created
      • refund.updated
      • refund.failed
      • charge.refunded
    • Copy the Signing Secret (starts with whsec_)
  4. Test with Stripe CLI (optional, for local development):

    stripe listen --forward-to localhost:3001/api/payments/webhook
    

Refund Policy Notes

  • Stripe does not enforce refund timing. The “7-day money-back guarantee” is a product policy.
  • Use the refund events above to keep payment records in sync when refunds happen.

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: Start with Caddy

Use both compose files together:

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

This will:

  • Build the frontend inside Docker with production URLs baked in
  • 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 3: Verify

Check that all services are running:

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

View frontend/Caddy logs:

docker logs kaboot-frontend

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.