- 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
8.5 KiB
DigiServer v2 - Quick Deployment Guide
📋 Overview
DigiServer is deployed using Docker Compose with the following architecture:
Internet (User) — Port 8080 (HTTP) / 8443 (HTTPS)
↓
Caddy Reverse Proxy (auto HTTPS, gzip, security headers)
↓
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
Caddyfilereplaces the verbosenginx.conf - Built-in HTTP/2, gzip, security headers
- Admin API (port 2019) — Flask reloads Caddy config dynamically
🚀 Quick Start
1️⃣ Clone & Setup
git clone <repository>
cd digiserver-v2
2️⃣ Start Containers
docker compose up -d
This automatically:
- ✅ Builds the Flask app image from
Dockerfile - ✅ Creates persistent data directories (
data/instance,data/uploads, etc.) - ✅ Starts Caddy reverse proxy on ports 8080 (HTTP) and 8443 (HTTPS)
- ✅ Initializes the database on first run (auto-creates admin user)
- ✅ Runs all required migrations
Access the app at: http://localhost:8080
3️⃣ First-Time Login
URL: http://localhost:8080
Username: admin
Password: admin123
⚠️ CHANGE PASSWORD IMMEDIATELY IN PRODUCTION!
📦 Container Architecture
Container 1: digiserver-app (Flask)
| 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 |
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 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 |
🚀 Full Automated Deployment
./deploy.sh
This runs the complete workflow:
- Creates
data/directories (instance,uploads,caddy-*) - Starts containers (
docker compose up -d) - Initialises database and admin user
- Runs all database migrations
- Enables HTTPS via Flask Admin
- Validates Caddy configuration
- Prints access URLs and credentials
🔧 Deployment Steps (Manual)
Step 1: Build & Start
docker compose up -d
- Builds Flask app image (layer cached)
- Creates
digiserver-networkbridge - Starts
digiserver-app, thencaddy
Step 2: Database Initialization (docker-entrypoint.sh)
On container start the entrypoint automatically:
- Creates required directories
- Checks for existing
dashboard.db - If absent — creates tables and seeds admin user
- Starts Gunicorn (4 workers, 120s timeout)
Step 3: Run Migrations
docker compose exec -T digiserver-app python /app/migrations/<name>.py
Key migrations:
add_https_config_table.py— HTTPS settings tableadd_player_user_table.py— Player user managementadd_email_to_https_config.py— Email fieldmigrate_player_user_global.py— Global settingsadd_url_to_content.py— Web link supportadd_deployment_fields_to_player.py— Deployment tracking
Step 4: Configure HTTPS
Via Admin UI (recommended):
- Go to Admin → HTTPS Config
- Enter hostname, domain, email, IP, port
- Caddy auto-provisions a Let's Encrypt certificate
Via CLI:
docker compose exec -T digiserver-app python /app/https_manager.py enable \
<hostname> <domain> <email> <ip_address> <port>
Step 5: ProxyFix Middleware (app/app.py)
Flask extracts real client info from Caddy headers:
X-Forwarded-For→ Real client IPX-Forwarded-Proto→ Protocol (http/https)X-Forwarded-Host→ Original hostnameX-Forwarded-Port→ Original port
🔄 Rebuild After Code Changes
Because the application code is baked into the Docker image, update it with:
# 1. Rebuild the image
docker build --no-cache -t digiserver-v2-digiserver-app .
# 2. Replace the running container
docker stop digiserver-v2 && docker rm digiserver-v2
docker compose up -d digiserver-app
Quick test (no rebuild) — copy files into the running container:
docker cp <local_file> digiserver-v2:/app/<path>
docker exec digiserver-v2 pkill -HUP -f gunicorn
🌐 Network Migration
When the server moves to a different network / IP:
./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
Containers
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
Logs
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
Status
docker compose ps
docker ps --format="table {{.Names}}\t{{.Status}}"
Database
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
Caddy
docker exec digiserver-caddy caddy validate --config /etc/caddy/Caddyfile
docker exec digiserver-caddy caddy reload --config /etc/caddy/Caddyfile
🔒 HTTPS / SSL
Caddy automatically provisions Let's Encrypt certificates when a public domain is configured. For development with self-signed certificates:
bash generate_nginx_certs.sh 192.168.0.121 365
docker compose restart caddy
Note:
generate_nginx_certs.shhas been moved toold_code_documentation/but still works.
🐛 Troubleshooting
Containers not starting?
docker compose logs
docker stats
App not responding?
docker compose ps
curl http://localhost:5000/ # Bypass Caddy, hit Flask directly
docker compose logs -f digiserver-app
Caddy / HTTPS issues?
docker exec digiserver-caddy caddy validate --config /etc/caddy/Caddyfile
ls -la ./data/caddy-data/certificates/
docker compose restart caddy