Files
dashboard/DEPLOYMENT.md
Matthias Hochmeister f09748f4a1 inital
2026-02-23 17:08:58 +01:00

14 KiB

Production Deployment Guide

This guide walks you through deploying the Feuerwehr Dashboard in a production environment.

Table of Contents

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)
  • 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:

# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y

# CentOS/RHEL
sudo yum update -y

Install Docker:

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

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:

# 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

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:

# Generate JWT secret (save this!)
openssl rand -base64 32

# Generate strong database password (save this!)
openssl rand -base64 24

Create .env file:

cp .env.example .env
nano .env

Configure the following critical variables:

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

chmod 600 .env

Step 4: Initial Deployment

Deploy the application:

make prod
# or
./deploy.sh production

Verify all services are running:

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:

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:

docker exec -it feuerwehr_db_prod psql -U prod_user -d feuerwehr_prod -c "\dt"

Step 6: Test the Deployment

Test health endpoints:

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

Create Caddyfile:

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:

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:

sudo apt install nginx certbot python3-certbot-nginx -y

Create Nginx configuration (/etc/nginx/sites-available/feuerwehr):

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:

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):

#!/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:

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:

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

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):

#!/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:

chmod +x /opt/monitor-feuerwehr.sh
crontab -e
*/5 * * * * /opt/monitor-feuerwehr.sh >> /var/log/feuerwehr-monitor.log 2>&1
  • 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:

    /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:

    git pull origin main
    
  3. Review changes:

    git log --oneline -10
    git diff HEAD~1 .env.example
    
  4. Update environment if needed:

    # Compare .env.example with your .env
    diff .env.example .env
    # Add any new required variables
    
  5. Rebuild and deploy:

    make rebuild
    # or
    ./deploy.sh rebuild
    
  6. Verify update:

    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:

    docker-compose down
    
  2. Restore previous version:

    git log --oneline -5  # Find previous commit
    git checkout <previous-commit-hash>
    
  3. Restore database if schema changed:

    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:

    make prod
    

Tagged Releases

Use Git tags for stable releases:

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

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:

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

backend:
  deploy:
    resources:
      limits:
        cpus: '2'
        memory: 2G
      reservations:
        cpus: '0.5'
        memory: 512M

Security Hardening

Docker Security

Run containers as non-root:

backend:
  user: "node"

postgres:
  user: "postgres"

Network Security

Isolate database from external access:

postgres:
  # Remove ports mapping for production
  # ports:
  #   - "5432:5432"
  # Database is only accessible via Docker network

Environment Security

Protect sensitive files:

chmod 600 .env
chmod 600 docker-compose.yml

Regular Security Updates

Set up automatic security updates:

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

docker-compose logs

Check resources:

docker stats
df -h
free -h

SSL Certificate Issues

Renew certificates:

sudo certbot renew

Performance Issues

Check resource usage:

docker stats
htop
iotop

Optimize database:

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 section
  4. Create detailed issue report
  5. Contact support team

Additional Resources