# py-dvt-ate Deployment Deploy the DVT Simulation Platform dashboard with Cloudflare Tunnel for public access. ## Idle Auto-Shutdown The app automatically shuts down after a period of inactivity to save resources. **Configuration:** ```bash # In .env or docker-compose.yml IDLE_TIMEOUT_MINUTES=30 # Shutdown after 30 min idle (0 = disabled) ``` **Behaviour:** - App tracks activity when someone has the dashboard open - After `IDLE_TIMEOUT_MINUTES` with no viewers, the container exits - nginx will show a 502 error until the container is restarted **Restart Options:** 1. **Manual restart** (simplest): ```bash docker start py-dvt-ate-streamlit ``` 2. **Auto-restart on poll** (uncomment `wakeup` service in docker-compose.yml): - Checks every 30 seconds if streamlit is stopped - Automatically restarts it - Adds ~30 second delay before app is available 3. **Always running** (set `IDLE_TIMEOUT_MINUTES=0`): - App never auto-shuts down - Uses minimal CPU when idle (~0.1%) - Memory stays allocated (~300-400MB) ## Architecture ``` Internet ↓ Cloudflare Edge (dvt-demo.kschappell.com) ↓ (tunnel) cloudflared container ↓ nginx container (WebSocket proxy + header handling) ↓ streamlit container (port 8080) ``` ## Directory Structure ``` /mnt/fast-pool/apps/portfolio-demos/py-dvt-ate/ ├── docker-compose.yml ├── nginx.conf ├── .env └── data/ # Persistent storage (created automatically) ├── py_dvt_ate.db # SQLite database ├── measurements/ # Test measurement files └── reports/ # Generated PDFs ``` ## Setup ### 1. Create Cloudflare Tunnel 1. Go to [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/) 2. Navigate to **Networks** → **Tunnels** 3. Click **Create a tunnel** 4. Select **Cloudflared** as the connector 5. Name it `py-dvt-ate` (or similar) 6. Copy the tunnel token (long string starting with `eyJ...`) ### 2. Configure Public Hostname Still in the tunnel configuration: 1. Go to the **Public Hostname** tab 2. Add a public hostname: - **Subdomain:** `dvt-demo` - **Domain:** `kschappell.com` - **Service Type:** `HTTP` - **URL:** `nginx:80` This routes `dvt-demo.kschappell.com` → nginx container → Streamlit app. ### 3. Deploy The build requires access to the full py-dvt-ate source code. Two options: **Option A: Clone repo to TrueNAS (recommended)** ```bash # Clone repo to apps directory cd /mnt/fast-pool/apps/portfolio-demos git clone https://gitea.kschappell.com/kschappell/py-dvt-ate.git # Create data directory and .env cd py-dvt-ate/deploy mkdir -p data cp .env.example .env nano .env # Add your CLOUDFLARE_TUNNEL_TOKEN # Build and start docker compose up -d --build ``` **Option B: Build image locally, transfer to TrueNAS** ```bash # On development machine cd /path/to/py-dvt-ate docker build -t py-dvt-ate:latest . docker save py-dvt-ate:latest | gzip > py-dvt-ate.tar.gz # Transfer to TrueNAS, then: docker load < py-dvt-ate.tar.gz # Update docker-compose.yml to use image instead of build: # streamlit: # image: py-dvt-ate:latest ``` **Check logs:** ```bash docker compose logs -f ``` ### 4. Verify ```bash # Check tunnel is connected (in Cloudflare dashboard, tunnel should show "Healthy") # Test the endpoint curl https://dvt-demo.kschappell.com/health # Test iframe headers curl -I https://dvt-demo.kschappell.com | grep -i frame # Should NOT show X-Frame-Options (we strip it) ``` ## Troubleshooting ### Tunnel not connecting Check cloudflared logs: ```bash docker compose logs cloudflared ``` Common issues: - Invalid token (regenerate in Cloudflare dashboard) - Network/firewall blocking outbound connections ### Streamlit not loading in iframe Check nginx is stripping headers: ```bash curl -I https://dvt-demo.kschappell.com ``` Should see: - No `X-Frame-Options` header - `Content-Security-Policy: frame-ancestors 'self' https://kschappell.com ...` ### WebSocket errors Check browser console for WebSocket connection failures. Ensure nginx WebSocket config is correct and timeouts are sufficient. ## Updating ```bash cd /mnt/fast-pool/apps/portfolio-demos/py-dvt-ate/deploy git pull docker compose up -d --build ``` ## Stopping ```bash docker compose down ```