Files
gems/docs/deployment.md
T
joakim 7ea78d3b54 docs: add comprehensive deployment guide and Caddy configuration
- Create detailed deployment documentation
- Add Caddyfile.example with security headers and API proxy
- Document SystemD service setup for Go API
- Include database backup strategy
- Add troubleshooting guide
- Document OAuth configuration steps
- Add build and deployment commands
- Test production build successfully (340KB static site)
2026-01-06 16:19:34 +01:00

351 lines
6.9 KiB
Markdown

# Opal-Web Deployment Guide
## Overview
This guide covers deploying the Opal Task Manager PWA to production on `opal.example.com` with Caddy as the web server.
## Prerequisites
- Server with Caddy installed
- Domain: `opal.example.com` (DNS configured)
- Authentik OAuth configured at `auth.example.com`
- Opal Go API server binary built
## Architecture
```
Browser → Caddy (opal.example.com)
├─ / → Static PWA (SvelteKit build)
└─ /api/* → Go API Server (:8080)
```
## Step 1: Build the Frontend
### Local Build
```bash
cd opal-web
# Install dependencies if needed
bun install
# Build for production
bun run build
# Output will be in: build/
```
The build creates a static site ready to deploy.
## Step 2: Deploy Static Files
### Option A: Direct Copy
```bash
# On your local machine
rsync -avz --delete build/ user@server:/var/www/opal/
```
### Option B: Server-Side Build
```bash
# On the server
cd /opt/opal-web
git pull
bun install
bun run build
cp -r build/* /var/www/opal/
```
### Set Permissions
```bash
sudo chown -R www-data:www-data /var/www/opal
sudo chmod -R 755 /var/www/opal
```
## Step 3: Configure Backend
### Create Environment File
```bash
sudo mkdir -p /etc/opal
sudo nano /etc/opal/opal.env
```
Add:
```bash
# Server
SERVER_ADDR=:8080
# Database
OPAL_DB_PATH=/var/lib/opal/opal.db
# OAuth (from Authentik setup)
OAUTH_ENABLED=true
OAUTH_ISSUER=https://auth.example.com/application/o/opal/
OAUTH_CLIENT_ID=your_client_id_from_authentik
OAUTH_CLIENT_SECRET=your_client_secret_from_authentik
OAUTH_REDIRECT_URI=https://opal.example.com/auth/callback
# JWT (generate with: openssl rand -hex 32)
JWT_SECRET=your_generated_jwt_secret_here
JWT_EXPIRY=3600
# Refresh Token
REFRESH_TOKEN_EXPIRY=604800
```
### Create SystemD Service
```bash
sudo nano /etc/systemd/system/opal-api.service
```
```ini
[Unit]
Description=Opal Task API Server
After=network.target
[Service]
Type=simple
User=opal
Group=opal
WorkingDirectory=/var/lib/opal
EnvironmentFile=/etc/opal/opal.env
ExecStart=/usr/local/bin/opal server start --addr :8080 --db /var/lib/opal/opal.db
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/opal
[Install]
WantedBy=multi-user.target
```
### Setup Database and User
```bash
# Create user
sudo useradd -r -s /bin/false opal
# Create directories
sudo mkdir -p /var/lib/opal
sudo chown opal:opal /var/lib/opal
sudo chmod 750 /var/lib/opal
# Build and install binary
cd opal-task
go build -o opal main.go
sudo cp opal /usr/local/bin/
sudo chmod 755 /usr/local/bin/opal
# Generate first API key (optional - for CLI access)
sudo -u opal opal server keygen --name "Admin CLI" --db /var/lib/opal/opal.db
# Save the generated key!
# Start service
sudo systemctl daemon-reload
sudo systemctl enable opal-api
sudo systemctl start opal-api
sudo systemctl status opal-api
```
## Step 4: Configure Caddy
### Create Caddyfile
```bash
sudo nano /etc/caddy/Caddyfile
```
Add:
```caddy
opal.example.com {
# Root directory for static PWA
root * /var/www/opal
# API proxy
handle /api/* {
uri strip_prefix /api
reverse_proxy localhost:8080
}
# Static file serving with SPA fallback
handle {
try_files {path} /index.html
file_server
}
# Security headers
header {
# HTTPS only
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking
X-Frame-Options "SAMEORIGIN"
# XSS protection
X-Content-Type-Options "nosniff"
# CSP (adjust as needed)
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://auth.example.com;"
}
# Logging
log {
output file /var/log/caddy/opal.log
format json
}
# Compression
encode gzip
}
```
### Test and Reload Caddy
```bash
# Test configuration
sudo caddy validate --config /etc/caddy/Caddyfile
# Reload
sudo systemctl reload caddy
# Check status
sudo systemctl status caddy
```
## Step 5: Verify Deployment
### Check Services
```bash
# API server
sudo systemctl status opal-api
sudo journalctl -u opal-api -n 50
# Caddy
sudo systemctl status caddy
sudo journalctl -u caddy -n 50
```
### Test Endpoints
```bash
# Health check
curl https://opal.example.com/api/health
# OAuth login URL
curl https://opal.example.com/api/auth/login
# Static files
curl -I https://opal.example.com/
```
### Browser Testing
1. Navigate to `https://opal.example.com`
2. Should see the Opal login page
3. Click "Login with OAuth"
4. Should redirect to Authentik
5. After login, should redirect back to task list
6. Test creating a task
7. Test PWA install prompt (mobile/desktop)
## Step 6: Update Deployment (Future)
```bash
# Frontend update
cd opal-web
git pull
bun install
bun run build
rsync -avz --delete build/ server:/var/www/opal/
# Backend update
cd opal-task
git pull
go build -o opal main.go
scp opal server:/tmp/
ssh server "sudo systemctl stop opal-api && sudo cp /tmp/opal /usr/local/bin/ && sudo systemctl start opal-api"
```
## Troubleshooting
### API Not Responding
```bash
# Check if running
sudo systemctl status opal-api
# Check logs
sudo journalctl -u opal-api -f
# Test locally
curl http://localhost:8080/health
```
### OAuth Not Working
- Verify redirect URI in Authentik matches exactly
- Check OAuth client ID and secret in env file
- Ensure JWT secret is set
- Check Caddy is proxying /api/* correctly
### PWA Not Installing
- Ensure HTTPS is working
- Check manifest.json is accessible
- Verify service worker is registered (Browser DevTools → Application)
- Icons must be served correctly
### Static Files 404
- Check file permissions in /var/www/opal
- Verify Caddy root directive points to correct path
- Ensure index.html exists in root
## Monitoring
### Logs
```bash
# API logs
sudo journalctl -u opal-api -f
# Caddy logs
sudo tail -f /var/log/caddy/opal.log
# System logs
dmesg | grep opal
```
### Database Backup
```bash
# Backup script
#!/bin/bash
BACKUP_DIR="/var/backups/opal"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
sudo -u opal sqlite3 /var/lib/opal/opal.db ".backup '$BACKUP_DIR/opal_$DATE.db'"
# Keep last 7 days
find "$BACKUP_DIR" -name "opal_*.db" -mtime +7 -delete
```
Add to crontab:
```bash
0 2 * * * /usr/local/bin/backup-opal.sh
```
## Security Checklist
- ✅ HTTPS enforced (Caddy auto)
- ✅ OAuth2 authentication configured
- ✅ JWT tokens with expiry
- ✅ API key hashing (bcrypt)
- ✅ SystemD service hardening
- ✅ Database file permissions (750)
- ✅ Security headers (CSP, HSTS, etc.)
- ✅ Regular backups configured
- ⬜ Fail2ban for API rate limiting (optional)
- ⬜ Log monitoring/alerting (optional)
## Performance Tips
- Caddy handles compression and HTTP/2
- Service worker caches static assets
- API responses cached by service worker
- Consider CDN for static assets (future)
## Support
- Check logs first
- Verify all environment variables
- Test OAuth flow in incognito
- Check network tab in DevTools