From 052751b599877815b8dafcfcca029420f0353969 Mon Sep 17 00:00:00 2001 From: scheianu Date: Tue, 21 Jul 2026 10:09:26 +0300 Subject: [PATCH] Rewrite QUICK_DEPLOYMENT.md for Caddy-based architecture - Replace Nginx references throughout with Caddy - Update architecture diagram to show Caddy - Update container details (caddy:2-alpine, ports 8080/8443) - Update volume paths (caddy-data, caddy-config, caddy-logs) - Add rebuild guide for code changes (docker build + restart) - Add quick-test method (docker cp + pkill -HUP) - Simplify commands section (docker compose not docker-compose) - Remove Nginx-specific troubleshooting - Add Caddy validation and reload commands --- QUICK_DEPLOYMENT.md | 636 +++++++++++++------------------------------- 1 file changed, 186 insertions(+), 450 deletions(-) diff --git a/QUICK_DEPLOYMENT.md b/QUICK_DEPLOYMENT.md index 289ac74..ae4da47 100644 --- a/QUICK_DEPLOYMENT.md +++ b/QUICK_DEPLOYMENT.md @@ -5,188 +5,156 @@ DigiServer is deployed using Docker Compose with the following architecture: ``` -Internet (User) +Internet (User) — Port 8080 (HTTP) / 8443 (HTTPS) ↓ -Nginx Reverse Proxy (Port 80/443) +Caddy Reverse Proxy (auto HTTPS, gzip, security headers) ↓ -Internal Docker Network +Internal Docker Network (digiserver-network) ↓ Flask App (Gunicorn on Port 5000) ↓ SQLite Database ``` +Caddy is used instead of Nginx because it: +- **Auto-provisions Let's Encrypt certificates** — no manual certbot +- **Simpler config** — a `Caddyfile` replaces the verbose `nginx.conf` +- **Built-in HTTP/2, gzip, security headers** +- **Admin API** (port 2019) — Flask reloads Caddy config dynamically + --- -## 🚀 Complete Deployment Workflow +## 🚀 Quick Start ### **1️⃣ Clone & Setup** ```bash -# Copy the app folder from repository git clone cd digiserver-v2 - -# Copy environment file and modify as needed -cp .env.example .env - -# Edit .env with your configuration: -nano .env ``` -**Configure in .env:** -```env -SECRET_KEY=your-secret-key-change-this -ADMIN_USERNAME=admin -ADMIN_PASSWORD=your-secure-password -DOMAIN=your-domain.com -EMAIL=admin@your-domain.com -IP_ADDRESS=192.168.0.111 -``` - ---- - -### **2️⃣ Deploy via Script** +### **2️⃣ Start Containers** ```bash -# Run the deployment script -./deploy.sh +docker compose up -d ``` This automatically: -1. ✅ Creates `data/` directories (instance, uploads, nginx-ssl, etc.) -2. ✅ Copies nginx configs from repo root to `data/` -3. ✅ Starts Docker containers -4. ✅ Initializes database -5. ✅ Runs all migrations -6. ✅ Configures HTTPS with SSL certificates -7. ✅ Displays access information +1. ✅ Builds the Flask app image from `Dockerfile` +2. ✅ Creates persistent data directories (`data/instance`, `data/uploads`, etc.) +3. ✅ Starts Caddy reverse proxy on ports **8080** (HTTP) and **8443** (HTTPS) +4. ✅ Initializes the database on first run (auto-creates admin user) +5. ✅ Runs all required migrations -**Output shows:** -- Access URLs (HTTP/HTTPS) -- Default credentials -- Next steps for configuration +**Access the app at:** `http://localhost:8080` ---- - -### **3️⃣ Network Migration (When Network Changes)** - -When moving the server to a different network with a new IP: - -```bash -# Migrate to the new network IP -./migrate_network.sh 10.55.150.160 - -# Optional: with custom hostname -./migrate_network.sh 10.55.150.160 digiserver-secured +### **3️⃣ First-Time Login** ``` - -This automatically: -1. ✅ Regenerates SSL certificates for new IP -2. ✅ Updates database HTTPS configuration -3. ✅ Restarts nginx and app containers -4. ✅ Verifies HTTPS connectivity - ---- - -### **4️⃣ Normal Operations** - -**Restart containers:** -```bash -docker compose restart -``` - -**Stop containers:** -```bash -docker compose down -``` - -**View logs:** -```bash -docker compose logs -f -``` - -**View container status:** -```bash -docker compose ps +URL: http://localhost:8080 +Username: admin +Password: admin123 ``` +⚠️ **CHANGE PASSWORD IMMEDIATELY IN PRODUCTION!** --- ## 📦 Container Architecture ### **Container 1: digiserver-app (Flask)** -- **Image**: Built from Dockerfile (Python 3.13) -- **Port**: 5000 (internal only) -- **Volumes**: - - `./data:/app` - Persistent application data - - `./data/instance:/app/instance` - Database & configuration - - `./data/uploads:/app/app/static/uploads` - User uploads -- **Startup**: Automatically initializes database on first run -- **Health Check**: Every 30 seconds +| Property | Value | +|----------|-------| +| **Image** | Built from `Dockerfile` (Python 3.13-slim) | +| **Container name** | `digiserver-v2` | +| **Exposed port** | `5000` (internal — proxied by Caddy) | +| **Mapped port** | `5000:5000` (for direct dev access) | +| **Entrypoint** | `docker-entrypoint.sh` (init DB → gunicorn) | +| **Workers** | 4 gunicorn workers, 120s timeout | -### **Container 2: nginx (Reverse Proxy)** -- **Image**: nginx:alpine -- **Ports**: 80 & 443 (exposed to internet) -- **Volumes**: - - `nginx.conf` - Main configuration - - `./data/nginx-ssl/` - SSL certificates - - `./data/nginx-logs/` - Access/error logs - - `./data/certbot/` - Let's Encrypt challenges -- **Startup**: Waits for Flask app to start -- **Health Check**: Every 30 seconds +**Volumes (persistent data):** +| Host path | Container path | Purpose | +|-----------|---------------|---------| +| `./data/instance` | `/app/instance` | SQLite database | +| `./data/uploads` | `/app/app/static/uploads` | Media uploads | + +> **Code is baked into the Docker image** — see [Rebuild](#-rebuild-after-code-changes) below. + +### **Container 2: caddy (Reverse Proxy)** +| Property | Value | +|----------|-------| +| **Image** | `caddy:2-alpine` | +| **Container name** | `digiserver-caddy` | +| **HTTP** | `8080 → 80` (inside container) | +| **HTTPS** | `8443 → 443` (inside container) | +| **Admin API** | `2019` (internal, for dynamic reloads) | + +**Volumes:** +| Host path | Container path | Purpose | +|-----------|---------------|---------| +| `./data/Caddyfile` | `/etc/caddy/Caddyfile:rw` | Caddy config (rewritable by Flask) | +| `./data/caddy-data` | `/data` | Let's Encrypt certs & keys | +| `./data/caddy-config` | `/config` | Caddy JSON config | +| `./data/caddy-logs` | `/var/log/caddy` | Access logs | --- -## 🔧 Deployment Steps Explained +## 🚀 Full Automated Deployment -### **Step 1: Start Containers** ```bash -docker-compose up -d +./deploy.sh ``` -- Builds Flask app image (if needed) -- Starts both containers -- Waits for containers to be healthy + +This runs the complete workflow: +1. Creates `data/` directories (`instance`, `uploads`, `caddy-*`) +2. Starts containers (`docker compose up -d`) +3. Initialises database and admin user +4. Runs all database migrations +5. Enables HTTPS via Flask Admin +6. Validates Caddy configuration +7. Prints access URLs and credentials + +--- + +## 🔧 Deployment Steps (Manual) + +### **Step 1: Build & Start** +```bash +docker compose up -d +``` +- Builds Flask app image (layer cached) +- Creates `digiserver-network` bridge +- Starts `digiserver-app`, then `caddy` ### **Step 2: Database Initialization** (docker-entrypoint.sh) -When Flask container starts: -1. Create required directories -2. Check if database exists -3. If NOT exists: - - Initialize SQLite database (dashboard.db) - - Create admin user from environment variables -4. Start Gunicorn server (4 workers, 120s timeout) +On container start the entrypoint automatically: +1. Creates required directories +2. Checks for existing `dashboard.db` +3. If absent — creates tables and seeds admin user +4. Starts Gunicorn (4 workers, 120s timeout) ### **Step 3: Run Migrations** ```bash -docker-compose exec -T digiserver-app python /app/migrations/[migration_name].py +docker compose exec -T digiserver-app python /app/migrations/.py ``` -Applied migrations: -- `add_https_config_table.py` - HTTPS settings -- `add_player_user_table.py` - Player user management -- `add_email_to_https_config.py` - Email configuration -- `migrate_player_user_global.py` - Global settings +Key migrations: +- `add_https_config_table.py` — HTTPS settings table +- `add_player_user_table.py` — Player user management +- `add_email_to_https_config.py` — Email field +- `migrate_player_user_global.py` — Global settings +- `add_url_to_content.py` — Web link support +- `add_deployment_fields_to_player.py` — Deployment tracking ### **Step 4: Configure HTTPS** -- SSL certificates stored in `./data/nginx-ssl/` -- Pre-generated self-signed certs for development -- Ready for Let's Encrypt integration +Via Admin UI (recommended): +1. Go to **Admin → HTTPS Config** +2. Enter hostname, domain, email, IP, port +3. Caddy auto-provisions a Let's Encrypt certificate -### **Step 5: Reverse Proxy Routing** (nginx.conf) -``` -HTTP (80): - • Redirect all traffic to HTTPS - • Allow ACME challenges for Let's Encrypt - -HTTPS (443): - • TLS 1.2+, HTTP/2 enabled - • Proxy all requests to Flask app - • Security headers added - • Gzip compression enabled - • Max upload size: 2GB - • Proxy timeout: 300s +Via CLI: +```bash +docker compose exec -T digiserver-app python /app/https_manager.py enable \ + ``` -### **Step 6: ProxyFix Middleware** (app/app.py) -Extracts real client information from Nginx headers: +### **Step 5: ProxyFix Middleware** (app/app.py) +Flask extracts real client info from Caddy headers: - `X-Forwarded-For` → Real client IP - `X-Forwarded-Proto` → Protocol (http/https) - `X-Forwarded-Host` → Original hostname @@ -194,173 +162,118 @@ Extracts real client information from Nginx headers: --- -## 📂 Directory Structure & Persistence +## 🔄 Rebuild After Code Changes -``` -/srv/digiserver-v2/ -├── app/ (Flask application code) -├── data/ (PERSISTENT - mounted as Docker volume) -│ ├── app/ (Copy of app/ for container) -│ ├── instance/ (dashboard.db - SQLite database) -│ ├── uploads/ (User uploaded files) -│ ├── nginx-ssl/ (SSL certificates) -│ ├── nginx-logs/ (Nginx logs) -│ └── certbot/ (Let's Encrypt challenges) -├── migrations/ (Database schema updates) -├── docker-compose.yml (Container orchestration) -├── Dockerfile (Flask app image definition) -├── nginx.conf (Reverse proxy configuration) -└── docker-entrypoint.sh (Container startup script) -``` - ---- - -## 🔐 Default Credentials - -``` -Username: admin -Password: admin123 -``` - -⚠️ **CHANGE IMMEDIATELY IN PRODUCTION!** - ---- - -## 🌐 Access Points - -After deployment, access the app at: -- `https://localhost` (if deployed locally) -- `https://192.168.0.121` (if deployed on server) -- `https://` (if DNS configured) - ---- - -## ⚙️ Environment Variables (Optional) - -Create `.env` file in project root: +Because the application code is baked into the Docker image, update it with: ```bash -# Network Configuration -HOSTNAME=digiserver -DOMAIN=digiserver.example.com -IP_ADDRESS=192.168.0.121 +# 1. Rebuild the image +docker build --no-cache -t digiserver-v2-digiserver-app . -# SSL/HTTPS -EMAIL=admin@example.com - -# Flask Configuration -SECRET_KEY=your-secret-key-here -FLASK_ENV=production - -# Admin User -ADMIN_USERNAME=admin -ADMIN_PASSWORD=admin123 +# 2. Replace the running container +docker stop digiserver-v2 && docker rm digiserver-v2 +docker compose up -d digiserver-app ``` -Then start with: +**Quick test (no rebuild)** — copy files into the running container: ```bash -docker-compose up -d +docker cp digiserver-v2:/app/ +docker exec digiserver-v2 pkill -HUP -f gunicorn +``` + +--- + +## 🌐 Network Migration + +When the server moves to a different network / IP: + +```bash +./migrate_network.sh 10.55.150.160 +# Optional: ./migrate_network.sh 10.55.150.160 digiserver-secured +``` + +This regenerates SSL certificates, updates the database HTTPS config, and restarts both containers. + +--- + +## 📂 Directory Structure + +``` +digiserver-v2/ +├── app/ (Flask application code) +│ ├── app.py (App factory) +│ ├── blueprints/ (Route definitions) +│ ├── models/ (SQLAlchemy models) +│ ├── templates/ (Jinja2 templates) +│ ├── static/uploads/ (Media files on disk) +│ └── utils/ (Helpers) +├── data/ (PERSISTENT — survives restarts) +│ ├── instance/dashboard.db (SQLite database) +│ ├── uploads/ (Media files) +│ ├── Caddyfile (Caddy config) +│ ├── caddy-data/ (Let's Encrypt certificates) +│ ├── caddy-config/ (Caddy JSON config) +│ └── caddy-logs/ (Access logs) +├── migrations/ (One-shot DB scripts) +├── old_code_documentation/ (Legacy Nginx files, etc.) +├── docker-compose.yml (Service definitions) +├── Dockerfile (Flask image build) +├── deploy.sh (Full automated deploy) +├── migrate_network.sh (Re-config after IP change) +└── docker-entrypoint.sh (Container startup) ``` --- ## 🛠️ Common Commands -### **Start/Stop Containers** +### **Containers** ```bash -# Start containers -docker-compose up -d - -# Stop containers -docker-compose down - -# Restart containers -docker-compose restart - -# Restart specific container -docker-compose restart digiserver-app -docker-compose restart nginx +docker compose up -d # Start all services +docker compose down # Stop all services +docker compose restart # Restart all +docker compose restart caddy # Restart only Caddy ``` -### **View Logs** +### **Logs** ```bash -# All containers -docker-compose logs - -# Follow logs (real-time) -docker-compose logs -f - -# Specific container -docker-compose logs -f digiserver-app -docker-compose logs -f nginx - -# Show last 50 lines -docker-compose logs --tail=50 digiserver-app +docker compose logs -f # Follow all +docker compose logs -f digiserver-app # Flask only +docker compose logs -f caddy # Caddy only +docker compose logs --tail=50 digiserver-app ``` -### **Container Status** +### **Status** ```bash -# Show running containers -docker-compose ps - -# Show container details -docker-compose ps -a - -# Check container health +docker compose ps docker ps --format="table {{.Names}}\t{{.Status}}" ``` -### **Database Operations** +### **Database** ```bash -# Access database shell -docker-compose exec digiserver-app sqlite3 /app/instance/dashboard.db - -# Backup database -docker-compose exec digiserver-app cp /app/instance/dashboard.db /app/instance/dashboard.db.backup - -# Restore database -docker-compose exec digiserver-app cp /app/instance/dashboard.db.backup /app/instance/dashboard.db +docker compose exec digiserver-app sqlite3 /app/instance/dashboard.db +docker compose exec digiserver-app cp /app/instance/dashboard.db \ + /app/instance/dashboard.db.backup ``` -### **Nginx Operations** +### **Caddy** ```bash -# Validate Nginx configuration -docker exec digiserver-nginx nginx -t - -# Reload Nginx (without restart) -docker exec digiserver-nginx nginx -s reload - -# View Nginx logs -docker-compose logs -f nginx +docker exec digiserver-caddy caddy validate --config /etc/caddy/Caddyfile +docker exec digiserver-caddy caddy reload --config /etc/caddy/Caddyfile ``` --- -## 🔒 SSL Certificate Management +## 🔒 HTTPS / SSL + +Caddy automatically provisions Let's Encrypt certificates when a public domain is configured. For development with self-signed certificates: -### **Generate New Self-Signed Certificate** ```bash bash generate_nginx_certs.sh 192.168.0.121 365 -docker-compose restart nginx +docker compose restart caddy ``` -Parameters: -- `192.168.0.121` - Domain/IP for certificate -- `365` - Certificate validity in days - -### **Set Up Let's Encrypt (Production)** -1. Update `DOMAIN` and `EMAIL` in environment -2. Modify `nginx.conf` to enable certbot challenges -3. Run certbot: - ```bash - docker run --rm -v $(pwd)/data/certbot:/etc/letsencrypt \ - -v $(pwd)/data/nginx-logs:/var/log/letsencrypt \ - certbot/certbot certonly --webroot \ - -w /var/www/certbot \ - -d yourdomain.com \ - -m your-email@example.com \ - --agree-tos - ``` +> **Note:** `generate_nginx_certs.sh` has been moved to `old_code_documentation/` but still works. --- @@ -368,197 +281,20 @@ Parameters: ### **Containers not starting?** ```bash -# Check docker-compose logs -docker-compose logs - -# Check system resources +docker compose logs docker stats - -# Restart Docker daemon -sudo systemctl restart docker ``` -### **Application not responding?** +### **App not responding?** ```bash -# Check app container health -docker-compose ps - -# View app logs -docker-compose logs -f digiserver-app - -# Test Flask directly -docker-compose exec digiserver-app curl http://localhost:5000/ +docker compose ps +curl http://localhost:5000/ # Bypass Caddy, hit Flask directly +docker compose logs -f digiserver-app ``` -### **HTTPS not working?** +### **Caddy / HTTPS issues?** ```bash -# Verify Nginx config -docker exec digiserver-nginx nginx -t - -# Check SSL certificates exist -ls -la ./data/nginx-ssl/ - -# View Nginx error logs -docker-compose logs nginx +docker exec digiserver-caddy caddy validate --config /etc/caddy/Caddyfile +ls -la ./data/caddy-data/certificates/ +docker compose restart caddy ``` - -### **Database issues?** -```bash -# Check database file exists -ls -la ./data/instance/dashboard.db - -# Verify database permissions -docker-compose exec digiserver-app ls -la /app/instance/ - -# Check database tables -docker-compose exec digiserver-app sqlite3 /app/instance/dashboard.db ".tables" -``` - -### **Port already in use?** -```bash -# Find process using port 80 -sudo lsof -i :80 - -# Find process using port 443 -sudo lsof -i :443 - -# Kill process (if needed) -sudo kill -9 -``` - ---- - -## 📊 Health Checks - -Both containers have health checks: - -**Flask App**: Pings `http://localhost:5000/` every 30 seconds -**Nginx**: Pings `http://localhost:80/` every 30 seconds - -Check health status: -```bash -docker-compose ps -# Look for "Up (healthy)" status -``` - ---- - -## 🔄 Database Backup & Restore - -### **Backup** -```bash -# Create backup -docker-compose exec digiserver-app cp /app/instance/dashboard.db /app/instance/dashboard.backup.db - -# Download to local machine -cp ./data/instance/dashboard.backup.db ./backup/ -``` - -### **Restore** -```bash -# Stop containers -docker-compose stop - -# Restore database -cp ./backup/dashboard.backup.db ./data/instance/dashboard.db - -# Start containers -docker-compose up -d -``` - ---- - -## 📈 Performance Tuning - -### **Gunicorn Workers** (docker-entrypoint.sh) -```bash -# Default: 4 workers -# Formula: (2 × CPU_count) + 1 -# For 4-core CPU: 9 workers - -# Modify docker-entrypoint.sh: -gunicorn --workers 9 ... -``` - -### **Nginx Worker Processes** (nginx.conf) -```nginx -# Default: auto (CPU count) -worker_processes auto; - -# Or specify manually: -worker_processes 4; -``` - -### **Upload Timeout** (nginx.conf) -```nginx -# Default: 300s -proxy_connect_timeout 300s; -proxy_send_timeout 300s; -proxy_read_timeout 300s; -``` - ---- - -## 🔐 Security Checklist - -- [ ] Change admin password immediately -- [ ] Set strong `SECRET_KEY` environment variable -- [ ] Enable firewall rules (allow only ports 80, 443) -- [ ] Set up HTTPS with Let's Encrypt -- [ ] Configure regular database backups -- [ ] Review Nginx security headers -- [ ] Update Flask dependencies regularly -- [ ] Monitor container logs for errors -- [ ] Restrict admin panel access (IP whitelist optional) -- [ ] Enable Flask debug mode only in development - ---- - -## 📞 Support & Documentation - -- **Nginx Setup**: See `NGINX_SETUP_QUICK.md` -- **ProxyFix Configuration**: See `PROXY_FIX_SETUP.md` -- **Deployment Commands**: See `DEPLOYMENT_COMMANDS.md` -- **Issue Troubleshooting**: Check `old_code_documentation/` - ---- - -## ✅ Deployment Checklist - -- [ ] Docker & Docker Compose installed -- [ ] Running from `/srv/digiserver-v2` directory -- [ ] Environment variables configured (optional) -- [ ] Port 80/443 available -- [ ] Sufficient disk space (min 5GB) -- [ ] Sufficient RAM (min 2GB free) -- [ ] Network connectivity verified -- [ ] SSL certificates generated or obtained -- [ ] Admin credentials changed (production) - ---- - -## 🚀 Next Steps After Deployment - -1. **Access Web Interface** - - Login with admin credentials - - Change password immediately - -2. **Configure Application** - - Set up players - - Upload content - - Configure groups & permissions - -3. **Production Hardening** - - Enable Let's Encrypt HTTPS - - Configure firewall rules - - Set up database backups - - Monitor logs - -4. **Optional Enhancements** - - Set up custom domain - - Configure email notifications - - Install optional dependencies (LibreOffice, etc.) - ---- - -**Last Updated**: January 15, 2026