kaboot/docs/PRODUCTION.md

8.7 KiB

Production Deployment Guide

This guide provides instructions for deploying Kaboot to a production environment. It covers security, persistence, and configuration for a robust setup.

Prerequisites

  • A Linux server with Docker and Docker Compose installed.
  • A registered domain name (e.g., kaboot.example.com).
  • SSL certificates (e.g., from Let's Encrypt).
  • A Google Gemini API key.

Architecture Overview

In production, the stack consists of:

  • Nginx: Reverse proxy handling HTTPS and routing.
  • Authentik: Identity Provider for authentication.
  • Kaboot Backend: Express server for quiz logic and SQLite storage.
  • Kaboot Frontend: Static assets served via Nginx or a dedicated service.
  • PostgreSQL: Database for Authentik.
  • Redis: Cache and task queue for Authentik.

Environment Variables

Create a production .env file. Do not commit this file to version control.

Backend & Authentik Configuration

# Database Passwords (Generate strong secrets)
PG_PASS=your_strong_postgres_password
AUTHENTIK_SECRET_KEY=your_strong_authentik_secret

# Infrastructure
AUTHENTIK_PORT_HTTP=9000
KABOOT_BACKEND_PORT=3001

# AI Configuration
GEMINI_API_KEY=your_gemini_api_key

# OIDC Production Settings
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
LOG_REQUESTS=true

Frontend Configuration

The frontend requires environment variables at build time:

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

Docker Compose Production Example

Create a docker-compose.prod.yml for your production environment:

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      interval: 30s
      timeout: 5s
      retries: 5
    volumes:
      - postgresql-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    networks:
      - kaboot-network

  redis:
    image: docker.io/library/redis:alpine
    restart: unless-stopped
    command: --save 60 1 --loglevel warning
    volumes:
      - redis-data:/data
    networks:
      - kaboot-network

  authentik-server:
    image: ghcr.io/goauthentik/server:2025.2
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    volumes:
      - ./authentik/media:/media
      - ./authentik/custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - kaboot-network

  authentik-worker:
    image: ghcr.io/goauthentik/server:2025.2
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./authentik/media:/media
      - ./authentik/certs:/certs
      - ./authentik/custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - kaboot-network

  kaboot-backend:
    build:
      context: ./server
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      NODE_ENV: production
      PORT: 3001
      DATABASE_PATH: /data/kaboot.db
      OIDC_ISSUER: ${OIDC_ISSUER}
      OIDC_JWKS_URI: ${OIDC_JWKS_URI}
      CORS_ORIGIN: ${CORS_ORIGIN}
      LOG_REQUESTS: ${LOG_REQUESTS}
    volumes:
      - kaboot-data:/data
    networks:
      - kaboot-network

volumes:
  postgresql-data:
  redis-data:
  kaboot-data:

networks:
  kaboot-network:
    driver: bridge

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
}

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.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.yml -f docker-compose.caddy.yml ps

View Caddy logs:

docker logs kaboot-caddy

Stopping the Stack

docker compose -f docker-compose.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

  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
  2. 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

  • Change all default passwords (PG_PASS, AUTHENTIK_SECRET_KEY).
  • Ensure NODE_ENV is set to production.
  • Use HTTPS for all connections.
  • Set CORS_ORIGIN to your specific frontend domain.
  • 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.