# 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: ```bash # 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: ```bash 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: ```bash # 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 ```bash # On your server, run: curl -4 ifconfig.me ``` ### Namecheap Setup 1. Log in to [Namecheap](https://www.namecheap.com/) 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](https://dash.cloudflare.com/) and select your domain 2. Go to **DNS** → **Records** 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**: `kaboot` → **Value/Points to**: `YOUR_SERVER_IP` - **Name/Host**: `auth` → **Value/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): ```bash # 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](https://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 ```env # 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: ```env # 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](https://dashboard.stripe.com/products): - 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](https://dashboard.stripe.com/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` - Copy the Signing Secret (starts with `whsec_`) 4. **Test with Stripe CLI** (optional, for local development): ```bash stripe listen --forward-to localhost:3001/api/payments/webhook ``` ## 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. ### 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 { 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: ```bash 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: ```bash docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml ps ``` View frontend/Caddy logs: ```bash docker logs kaboot-frontend ``` Check Authentik blueprint was applied: ```bash docker compose -f docker-compose.prod.yml logs authentik-server | grep -i blueprint ``` **Stopping the Stack** ```bash 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. ```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 ### Option A: Automated Setup (Recommended) The `setup-prod.sh` script handles all Authentik configuration automatically: ```bash ./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: ```bash # 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: ```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 - [ ] 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.