kaboot/docs/PRODUCTION.md

326 lines
8.7 KiB
Markdown

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