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
.envfile with domain configuration - Creates the
Caddyfilewith 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:
- URL:
https://auth.your-domain.com/if/admin/ - Username:
akadmin - Password: The password shown when you ran
setup-prod.sh(stored asAUTHENTIK_BOOTSTRAP_PASSWORDin.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
- Log in to Namecheap and go to Domain List
- Click Manage next to your domain
- Go to the Advanced DNS tab
- 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 justkaboot.
Cloudflare Setup
- Log in to Cloudflare and select your domain
- Go to DNS → Records
- 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:
- Find the DNS Management or DNS Settings section
- Add two A Records:
- Name/Host:
kaboot→ Value/Points to:YOUR_SERVER_IP - Name/Host:
auth→ Value/Points to:YOUR_SERVER_IP
- Name/Host:
- 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.
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:
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
Option A: Automated Setup (Recommended)
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
- 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/callbackhttps://kaboot.example.com/silent-renew.htmlhttps://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.shto generate strong secrets (don't use defaults). - Ensure
NODE_ENVis set toproduction. - Use HTTPS for all connections (Caddy handles this automatically).
- Set
CORS_ORIGINto your specific frontend domain (e.g.,https://kaboot.example.com). - Update
OIDC_ISSUERandOIDC_JWKS_URIto use your auth domain with HTTPS. - Regularly back up the
kaboot.dband 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.