inital
This commit is contained in:
704
DEPLOYMENT.md
Normal file
704
DEPLOYMENT.md
Normal file
@@ -0,0 +1,704 @@
|
||||
# Production Deployment Guide
|
||||
|
||||
This guide walks you through deploying the Feuerwehr Dashboard in a production environment.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Server Requirements](#server-requirements)
|
||||
- [Pre-Deployment Checklist](#pre-deployment-checklist)
|
||||
- [Deployment Steps](#deployment-steps)
|
||||
- [Authentik Configuration](#authentik-configuration)
|
||||
- [SSL/HTTPS Setup](#ssl-https-setup)
|
||||
- [Database Management](#database-management)
|
||||
- [Monitoring Setup](#monitoring-setup)
|
||||
- [Update Procedures](#update-procedures)
|
||||
- [Rollback Procedures](#rollback-procedures)
|
||||
- [Performance Tuning](#performance-tuning)
|
||||
- [Security Hardening](#security-hardening)
|
||||
|
||||
## Server Requirements
|
||||
|
||||
### Minimum Requirements
|
||||
|
||||
- **CPU**: 2 cores
|
||||
- **RAM**: 2 GB (4 GB recommended)
|
||||
- **Storage**: 20 GB SSD
|
||||
- **OS**: Ubuntu 20.04+ / Debian 11+ / CentOS 8+ / RHEL 8+
|
||||
- **Docker**: 20.10+
|
||||
- **Docker Compose**: 2.0+
|
||||
|
||||
### Network Requirements
|
||||
|
||||
Open the following ports:
|
||||
|
||||
- **80** - HTTP (will redirect to HTTPS)
|
||||
- **443** - HTTPS (if using SSL)
|
||||
- **3000** - Backend API (can be internal only)
|
||||
- **5432** - PostgreSQL (should be internal only)
|
||||
|
||||
### Recommended Production Specs
|
||||
|
||||
- **CPU**: 4 cores
|
||||
- **RAM**: 8 GB
|
||||
- **Storage**: 50 GB SSD with RAID for database
|
||||
- **Network**: 100 Mbps minimum
|
||||
|
||||
## Pre-Deployment Checklist
|
||||
|
||||
Before deploying to production:
|
||||
|
||||
- [ ] Server meets minimum requirements
|
||||
- [ ] Docker and Docker Compose installed
|
||||
- [ ] Domain name configured (DNS A record pointing to server)
|
||||
- [ ] Authentik instance available and accessible
|
||||
- [ ] SSL certificates obtained (Let's Encrypt or commercial)
|
||||
- [ ] Firewall configured
|
||||
- [ ] Backup strategy planned
|
||||
- [ ] Monitoring solution chosen
|
||||
- [ ] All secrets and credentials prepared
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
### Step 1: Server Preparation
|
||||
|
||||
Update system packages:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo yum update -y
|
||||
```
|
||||
|
||||
Install Docker:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo yum install -y docker
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
Install Docker Compose:
|
||||
|
||||
```bash
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
Configure firewall:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian (UFW)
|
||||
sudo ufw allow 22/tcp
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw enable
|
||||
|
||||
# CentOS/RHEL (firewalld)
|
||||
sudo firewall-cmd --permanent --add-service=http
|
||||
sudo firewall-cmd --permanent --add-service=https
|
||||
sudo firewall-cmd --permanent --add-service=ssh
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
### Step 2: Clone Repository
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
sudo git clone <your-repository-url> feuerwehr_dashboard
|
||||
cd feuerwehr_dashboard
|
||||
sudo chown -R $USER:$USER .
|
||||
```
|
||||
|
||||
### Step 3: Configure Environment
|
||||
|
||||
Generate secure secrets:
|
||||
|
||||
```bash
|
||||
# Generate JWT secret (save this!)
|
||||
openssl rand -base64 32
|
||||
|
||||
# Generate strong database password (save this!)
|
||||
openssl rand -base64 24
|
||||
```
|
||||
|
||||
Create `.env` file:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
Configure the following critical variables:
|
||||
|
||||
```bash
|
||||
# Database - Use strong passwords!
|
||||
POSTGRES_DB=feuerwehr_prod
|
||||
POSTGRES_USER=prod_user
|
||||
POSTGRES_PASSWORD=<generated-secure-password>
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Backend
|
||||
BACKEND_PORT=3000
|
||||
NODE_ENV=production
|
||||
|
||||
# JWT - Use generated secret!
|
||||
JWT_SECRET=<generated-jwt-secret>
|
||||
|
||||
# CORS - Set to your domain!
|
||||
CORS_ORIGIN=https://dashboard.yourdomain.com
|
||||
|
||||
# Frontend
|
||||
FRONTEND_PORT=80
|
||||
|
||||
# API URL - Set to your backend URL
|
||||
VITE_API_URL=https://api.yourdomain.com
|
||||
|
||||
# Authentik OAuth (from Authentik setup)
|
||||
AUTHENTIK_CLIENT_ID=<your-client-id>
|
||||
AUTHENTIK_CLIENT_SECRET=<your-client-secret>
|
||||
AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr/
|
||||
AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback
|
||||
```
|
||||
|
||||
Secure the .env file:
|
||||
|
||||
```bash
|
||||
chmod 600 .env
|
||||
```
|
||||
|
||||
### Step 4: Initial Deployment
|
||||
|
||||
Deploy the application:
|
||||
|
||||
```bash
|
||||
make prod
|
||||
# or
|
||||
./deploy.sh production
|
||||
```
|
||||
|
||||
Verify all services are running:
|
||||
|
||||
```bash
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
NAME STATUS PORTS
|
||||
feuerwehr_backend_prod Up (healthy) 0.0.0.0:3000->3000/tcp
|
||||
feuerwehr_db_prod Up (healthy) 0.0.0.0:5432->5432/tcp
|
||||
feuerwehr_frontend_prod Up (healthy) 0.0.0.0:80->80/tcp
|
||||
```
|
||||
|
||||
Check logs for errors:
|
||||
|
||||
```bash
|
||||
make logs-prod
|
||||
```
|
||||
|
||||
### Step 5: Database Initialization
|
||||
|
||||
The database will be automatically initialized on first start using the migration scripts in `backend/src/database/migrations/`.
|
||||
|
||||
Verify database tables:
|
||||
|
||||
```bash
|
||||
docker exec -it feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod -c "\dt"
|
||||
```
|
||||
|
||||
### Step 6: Test the Deployment
|
||||
|
||||
Test health endpoints:
|
||||
|
||||
```bash
|
||||
# Backend health check
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Frontend availability
|
||||
curl http://localhost:80
|
||||
```
|
||||
|
||||
Test authentication flow:
|
||||
1. Open browser to `http://localhost` (or your domain)
|
||||
2. Click login
|
||||
3. Authenticate with Authentik
|
||||
4. Verify redirect back to dashboard
|
||||
5. Verify user profile loads
|
||||
|
||||
## Authentik Configuration
|
||||
|
||||
See [AUTHENTIK_SETUP.md](AUTHENTIK_SETUP.md) for complete Authentik configuration.
|
||||
|
||||
Key points for production:
|
||||
|
||||
1. **Use HTTPS URLs** for all redirect URIs
|
||||
2. **Configure proper scopes**: `openid`, `profile`, `email`
|
||||
3. **Set token expiration** appropriately (e.g., 3600s for access, 86400s for refresh)
|
||||
4. **Enable required claims** in the provider
|
||||
5. **Test the flow** thoroughly before going live
|
||||
|
||||
## SSL/HTTPS Setup
|
||||
|
||||
### Option 1: Using Caddy (Recommended)
|
||||
|
||||
Create `Caddyfile`:
|
||||
|
||||
```caddy
|
||||
dashboard.yourdomain.com {
|
||||
reverse_proxy localhost:80
|
||||
encode gzip
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000;"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
}
|
||||
|
||||
api.yourdomain.com {
|
||||
reverse_proxy localhost:3000
|
||||
encode gzip
|
||||
}
|
||||
```
|
||||
|
||||
Run Caddy:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name caddy \
|
||||
--network host \
|
||||
-v $PWD/Caddyfile:/etc/caddy/Caddyfile \
|
||||
-v caddy_data:/data \
|
||||
-v caddy_config:/config \
|
||||
--restart unless-stopped \
|
||||
caddy:latest
|
||||
```
|
||||
|
||||
Caddy will automatically obtain and renew SSL certificates from Let's Encrypt.
|
||||
|
||||
### Option 2: Using Nginx with Certbot
|
||||
|
||||
Install Nginx and Certbot:
|
||||
|
||||
```bash
|
||||
sudo apt install nginx certbot python3-certbot-nginx -y
|
||||
```
|
||||
|
||||
Create Nginx configuration (`/etc/nginx/sites-available/feuerwehr`):
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name dashboard.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Enable and obtain SSL:
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/feuerwehr /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
sudo certbot --nginx -d dashboard.yourdomain.com -d api.yourdomain.com
|
||||
```
|
||||
|
||||
### Option 3: Using Docker with Traefik
|
||||
|
||||
See community guides for Traefik integration.
|
||||
|
||||
## Database Management
|
||||
|
||||
### Backup Procedures
|
||||
|
||||
Create automated backup script (`/opt/backup-feuerwehr-db.sh`):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/opt/backups/feuerwehr"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="$BACKUP_DIR/feuerwehr_backup_$DATE.sql.gz"
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
docker exec feuerwehr_db_prod pg_dump -U prod_user feuerwehr_prod | gzip > $BACKUP_FILE
|
||||
|
||||
# Keep only last 30 days of backups
|
||||
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
|
||||
|
||||
echo "Backup completed: $BACKUP_FILE"
|
||||
```
|
||||
|
||||
Make executable and schedule:
|
||||
|
||||
```bash
|
||||
chmod +x /opt/backup-feuerwehr-db.sh
|
||||
|
||||
# Add to crontab (daily at 2 AM)
|
||||
crontab -e
|
||||
0 2 * * * /opt/backup-feuerwehr-db.sh >> /var/log/feuerwehr-backup.log 2>&1
|
||||
```
|
||||
|
||||
### Restore Procedures
|
||||
|
||||
Restore from backup:
|
||||
|
||||
```bash
|
||||
# Stop backend service
|
||||
docker-compose stop backend
|
||||
|
||||
# Restore database
|
||||
gunzip -c /opt/backups/feuerwehr/feuerwehr_backup_20260223_020000.sql.gz | \
|
||||
docker exec -i feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod
|
||||
|
||||
# Restart backend
|
||||
docker-compose start backend
|
||||
```
|
||||
|
||||
### Database Maintenance
|
||||
|
||||
Regular vacuum and analyze:
|
||||
|
||||
```bash
|
||||
docker exec feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod -c "VACUUM ANALYZE;"
|
||||
```
|
||||
|
||||
## Monitoring Setup
|
||||
|
||||
### Basic Health Monitoring
|
||||
|
||||
Create monitoring script (`/opt/monitor-feuerwehr.sh`):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Check backend health
|
||||
BACKEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
|
||||
if [ "$BACKEND_STATUS" != "200" ]; then
|
||||
echo "Backend health check failed: $BACKEND_STATUS"
|
||||
# Send alert (email, Slack, etc.)
|
||||
fi
|
||||
|
||||
# Check database
|
||||
DB_STATUS=$(docker exec feuerwehr_db_prod pg_isready -U prod_user)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Database health check failed"
|
||||
# Send alert
|
||||
fi
|
||||
|
||||
# Check container status
|
||||
CONTAINERS=$(docker-compose ps --services --filter "status=running" | wc -l)
|
||||
if [ "$CONTAINERS" -lt 3 ]; then
|
||||
echo "Not all containers are running"
|
||||
# Send alert
|
||||
fi
|
||||
```
|
||||
|
||||
Schedule monitoring:
|
||||
|
||||
```bash
|
||||
chmod +x /opt/monitor-feuerwehr.sh
|
||||
crontab -e
|
||||
*/5 * * * * /opt/monitor-feuerwehr.sh >> /var/log/feuerwehr-monitor.log 2>&1
|
||||
```
|
||||
|
||||
### Recommended Monitoring Tools
|
||||
|
||||
- **Prometheus + Grafana** - Metrics and dashboards
|
||||
- **Loki** - Log aggregation
|
||||
- **Uptime Kuma** - Simple uptime monitoring
|
||||
- **Portainer** - Docker container management
|
||||
|
||||
## Update Procedures
|
||||
|
||||
### Standard Update Process
|
||||
|
||||
1. **Backup everything**:
|
||||
```bash
|
||||
/opt/backup-feuerwehr-db.sh
|
||||
docker-compose down
|
||||
tar -czf /opt/backups/feuerwehr_config_$(date +%Y%m%d).tar.gz .env docker-compose.yml
|
||||
```
|
||||
|
||||
2. **Pull latest changes**:
|
||||
```bash
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
3. **Review changes**:
|
||||
```bash
|
||||
git log --oneline -10
|
||||
git diff HEAD~1 .env.example
|
||||
```
|
||||
|
||||
4. **Update environment** if needed:
|
||||
```bash
|
||||
# Compare .env.example with your .env
|
||||
diff .env.example .env
|
||||
# Add any new required variables
|
||||
```
|
||||
|
||||
5. **Rebuild and deploy**:
|
||||
```bash
|
||||
make rebuild
|
||||
# or
|
||||
./deploy.sh rebuild
|
||||
```
|
||||
|
||||
6. **Verify update**:
|
||||
```bash
|
||||
docker-compose ps
|
||||
make logs-prod
|
||||
# Test critical functionality
|
||||
```
|
||||
|
||||
### Zero-Downtime Updates
|
||||
|
||||
For zero-downtime updates, use blue-green deployment:
|
||||
|
||||
1. Set up second environment
|
||||
2. Deploy new version to green
|
||||
3. Test green environment
|
||||
4. Switch traffic (using load balancer)
|
||||
5. Shut down blue environment
|
||||
|
||||
## Rollback Procedures
|
||||
|
||||
### Quick Rollback
|
||||
|
||||
If issues arise after update:
|
||||
|
||||
1. **Stop current version**:
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
2. **Restore previous version**:
|
||||
```bash
|
||||
git log --oneline -5 # Find previous commit
|
||||
git checkout <previous-commit-hash>
|
||||
```
|
||||
|
||||
3. **Restore database** if schema changed:
|
||||
```bash
|
||||
gunzip -c /opt/backups/feuerwehr/feuerwehr_backup_<timestamp>.sql.gz | \
|
||||
docker exec -i feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod
|
||||
```
|
||||
|
||||
4. **Redeploy**:
|
||||
```bash
|
||||
make prod
|
||||
```
|
||||
|
||||
### Tagged Releases
|
||||
|
||||
Use Git tags for stable releases:
|
||||
|
||||
```bash
|
||||
# Tag a release
|
||||
git tag -a v1.0.0 -m "Production release 1.0.0"
|
||||
git push origin v1.0.0
|
||||
|
||||
# Deploy specific release
|
||||
git checkout v1.0.0
|
||||
make prod
|
||||
```
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### Database Optimization
|
||||
|
||||
Edit PostgreSQL configuration in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
postgres:
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "max_connections=100"
|
||||
- "-c"
|
||||
- "shared_buffers=256MB"
|
||||
- "-c"
|
||||
- "effective_cache_size=1GB"
|
||||
- "-c"
|
||||
- "work_mem=4MB"
|
||||
```
|
||||
|
||||
### Backend Optimization
|
||||
|
||||
Add environment variables to backend:
|
||||
|
||||
```bash
|
||||
# In .env
|
||||
NODE_OPTIONS=--max-old-space-size=2048
|
||||
```
|
||||
|
||||
### Frontend Optimization
|
||||
|
||||
Already optimized:
|
||||
- Vite build optimization
|
||||
- Gzip compression in Nginx
|
||||
- Static asset caching
|
||||
|
||||
### Docker Resource Limits
|
||||
|
||||
Add resource limits to `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
## Security Hardening
|
||||
|
||||
### Docker Security
|
||||
|
||||
Run containers as non-root:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
user: "node"
|
||||
|
||||
postgres:
|
||||
user: "postgres"
|
||||
```
|
||||
|
||||
### Network Security
|
||||
|
||||
Isolate database from external access:
|
||||
|
||||
```yaml
|
||||
postgres:
|
||||
# Remove ports mapping for production
|
||||
# ports:
|
||||
# - "5432:5432"
|
||||
# Database is only accessible via Docker network
|
||||
```
|
||||
|
||||
### Environment Security
|
||||
|
||||
Protect sensitive files:
|
||||
|
||||
```bash
|
||||
chmod 600 .env
|
||||
chmod 600 docker-compose.yml
|
||||
```
|
||||
|
||||
### Regular Security Updates
|
||||
|
||||
Set up automatic security updates:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install unattended-upgrades
|
||||
sudo dpkg-reconfigure -plow unattended-upgrades
|
||||
```
|
||||
|
||||
### Security Monitoring
|
||||
|
||||
- Enable Docker security scanning
|
||||
- Monitor logs for suspicious activity
|
||||
- Set up fail2ban for SSH protection
|
||||
- Regular security audits
|
||||
|
||||
## Post-Deployment
|
||||
|
||||
After successful deployment:
|
||||
|
||||
1. **Document your setup** - Note any custom configurations
|
||||
2. **Train your team** - Ensure team knows how to use the system
|
||||
3. **Set up alerts** - Configure notifications for issues
|
||||
4. **Schedule maintenance** - Plan for regular updates and backups
|
||||
5. **Review security** - Regular security audits
|
||||
6. **Monitor performance** - Track metrics and optimize as needed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Services Won't Start
|
||||
|
||||
Check logs:
|
||||
```bash
|
||||
docker-compose logs
|
||||
```
|
||||
|
||||
Check resources:
|
||||
```bash
|
||||
docker stats
|
||||
df -h
|
||||
free -h
|
||||
```
|
||||
|
||||
### SSL Certificate Issues
|
||||
|
||||
Renew certificates:
|
||||
```bash
|
||||
sudo certbot renew
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
|
||||
Check resource usage:
|
||||
```bash
|
||||
docker stats
|
||||
htop
|
||||
iotop
|
||||
```
|
||||
|
||||
Optimize database:
|
||||
```bash
|
||||
docker exec feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod -c "VACUUM FULL ANALYZE;"
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For production support issues:
|
||||
|
||||
1. Check logs first
|
||||
2. Review this deployment guide
|
||||
3. Consult [TROUBLESHOOTING](README.md#troubleshooting) section
|
||||
4. Create detailed issue report
|
||||
5. Contact support team
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Docker Documentation](https://docs.docker.com/)
|
||||
- [PostgreSQL Performance Tuning](https://wiki.postgresql.org/wiki/Performance_Optimization)
|
||||
- [Node.js Production Best Practices](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)
|
||||
- [Authentik Documentation](https://goauthentik.io/docs/)
|
||||
Reference in New Issue
Block a user