326 lines
8.7 KiB
Markdown
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.
|