Files
digiserver-v2/docs/07-deployment.md
T

5.7 KiB

07 · Deployment

DigiServer v2 runs as a Docker Compose stack (app + Caddy) with optional remote SSH provisioning of players.


1. Architecture

flowchart LR
    subgraph Host["Docker Host"]
        subgraph Net["digiserver-network"]
            APP["digiserver-app\nFlask + Gunicorn :5000"]
            CAD["caddy:2-alpine\n:80 / :443"]
        end
        VOL1["./data/instance → /app/instance\n(SQLite DBs)"]
        VOL2["./data/uploads → /app/app/static/uploads"]
        VOL3["./data/Caddyfile → /etc/caddy/Caddyfile"]
        VOL4["./data/caddy-data → /data\n./data/caddy-config → /config"]
    end
    Browser["Browser"] --> CAD
    CAD --> APP
    APP --> VOL1
    APP --> VOL2
    CAD --> VOL3
    CAD --> VOL4
    APP -.SSH/rsync.-> Players["Remote player hosts"]

2. docker-compose.yml

Service digiserver-app

  • Build: . (Dockerfile, python:3.13-slim)
  • Ports: 5000:5000 (direct dev access; also expose: 5000)
  • Volumes: ./data/instance:/app/instance, ./data/uploads:/app/app/static/uploads
  • Env: FLASK_ENV=production, SECRET_KEY, ADMIN_USERNAME, ADMIN_PASSWORD
  • Healthcheck: HTTP GET http://localhost:5000/ (30s interval, 40s start)
  • Restart: unless-stopped

Service caddy

  • Image: caddy:2-alpine
  • Ports: 8080:80, 8443:443
  • Volumes: ./data/Caddyfile:/etc/caddy/Caddyfile:rw, ./data/caddy-data:/data, ./data/caddy-config:/config, ./data/caddy-logs:/var/log/caddy
  • Depends on: app (service started)
  • Healthcheck: wget on port 80

3. Dockerfile

Stage Content
Base python:3.13-slim
System deps poppler-utils, ffmpeg, libmagic1, sudo, fonts-noto-color-emoji, LibreOffice (core/impress/writer), sshpass, openssh-client, rsync, git
Python COPY requirements.txtpip install (cached layer), then COPY . .
App config FLASK_APP=app.app:create_app, FLASK_ENV=production, EXPOSE 5000
User Non-root appuser (UID 1000) with passwordless sudo limited to apt-get, install_libreoffice.sh, install_emoji_fonts.sh
Runtime HEALTHCHECK (HTTP 5000), ENTRYPOINT /docker-entrypoint.sh

4. docker-entrypoint.sh

  1. Create /app/instance and /app/app/static/uploads.
  2. If dashboard.db is missing: create app + db.create_all(), then create/update the admin user from ADMIN_USERNAME / ADMIN_PASSWORD.
  3. Start Gunicorn: --bind 0.0.0.0:5000 --workers 4 --timeout 120 app.app:create_app('production').

5. deploy.sh (One-Shot Deployment)

1. Validate compose + project; create data/ subdirs; copy nginx configs
2. docker compose up -d + verify containers "Up"
3. Run migration scripts (add_https_config_table, add_player_user_table,
   add_email_to_https_config, migrate_player_user_global,
   add_original_filename_to_content)
4. Run /app/https_manager.py enable <hostname> <domain> <email> <ip> <port>
   ⚠  https_manager.py is NOT in the current repo — this step needs attention
5. Verify DB tables via SQLAlchemy inspector
6. caddy validate + https_manager.py status; print access URLs + default creds

6. HTTPS Setup (Caddy)

HTTPS is configured through the Admin → HTTPS Configuration page, which:

  1. Saves HTTPSConfig (hostname, domain, IP, email, port, enabled).
  2. CaddyConfigGenerator.generate_caddyfile(config) picks a template:
    • HTTP-only:80 reverse proxy
    • Domain → automatic Let's Encrypt
    • IP → internal CA self-signed
  3. Writes /etc/caddy/Caddyfile and reloads Caddy via POST http://caddy:2019/load.

The current data/Caddyfile (HTTP mode) includes: admin API on 0.0.0.0:2019, :80 block → digiserver-app:5000, 2 GB body limit, gzip, security headers, access log.


7. verify-deployment.sh — Pre/Post-Deployment Checks

Sections checked (pass/fail/warn counters):

  • git status
  • .env / .env.example
  • Docker + Compose versions + compose config syntax
  • Dockerfile best practices (HEALTHCHECK, non-root, slim base)
  • requirements.txt critical packages + versions
  • migrations directory
  • SSL cert expiry (openssl)
  • Flask config (ProductionConfig, SESSION_COOKIE_SECURE)
  • nginx.conf checks
  • runtime container health
  • security best practices

⚠ Note: the script still references docker-compose (v1) and digiserver-nginx — the current stack uses Compose v2 + Caddy.


8. Player Deployment Pipeline

sequenceDiagram
    participant U as Admin
    participant A as App
    participant B as Background
    participant H as Player host
    U->>A: Add player (name, hostname, password/quickconnect)
    A->>B: background_player_deployment()
    B->>B: build/refresh staged code (PLAYER_CODE_DIR)
    B->>H: sshpass + ssh test
    B->>H: rsync code (or git clone/pull fallback)
    B->>H: write config/app_config.json (server_ip, quickconnect, https...)
    B->>H: temporary passwordless sudo
    B->>H: ./install.sh && ./start.sh
    B->>H: remove temp sudoers
    B-->>A: player.deployment_status = 'deployed' | 'failed'
    U->>A: poll /players/deployment-status

Statuses: pending → deploying → deployed | failed, with last_deployment_at/status/message persisted on the player row. Receiving player feedback also auto-marks deployment deployed.


9. Ports & Volumes Summary

Item Value
App port (container) 5000
App port (host, dev) 5000
Caddy HTTP 8080 → 80
Caddy HTTPS 8443 → 443
Caddy admin API 2019 (container)
DB volume ./data/instance
Uploads volume ./data/uploads
Caddy data/config/logs ./data/caddy-*

Next: 08 · Workflows