# 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 ```env # 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: ```yaml 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. ### Option 1: Caddy (Recommended) 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: ```bash cp Caddyfile.example Caddyfile ``` Edit `Caddyfile` and replace `kaboot.example.com` and `auth.example.com` with your actual domains: ```caddyfile 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** ```bash npm run build ``` This creates the `dist/` directory with production assets. **Step 3: Start with Caddy** Use both compose files together: ```bash 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: ```bash docker compose -f docker-compose.yml -f docker-compose.caddy.yml ps ``` View Caddy logs: ```bash docker logs kaboot-caddy ``` **Stopping the Stack** ```bash 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. ```nginx 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: ```bash 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: ```bash 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.