- IDLE_TIMEOUT_MINUTES env var (default 30 min) - restart: no policy so container stays stopped - Optional wakeup service for auto-restart - Document three restart options in readme
4.2 KiB
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:
# 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_MINUTESwith no viewers, the container exits - nginx will show a 502 error until the container is restarted
Restart Options:
-
Manual restart (simplest):
docker start py-dvt-ate-streamlit -
Auto-restart on poll (uncomment
wakeupservice in docker-compose.yml):- Checks every 30 seconds if streamlit is stopped
- Automatically restarts it
- Adds ~30 second delay before app is available
-
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
- Go to Cloudflare Zero Trust Dashboard
- Navigate to Networks → Tunnels
- Click Create a tunnel
- Select Cloudflared as the connector
- Name it
py-dvt-ate(or similar) - Copy the tunnel token (long string starting with
eyJ...)
2. Configure Public Hostname
Still in the tunnel configuration:
- Go to the Public Hostname tab
- Add a public hostname:
- Subdomain:
dvt-demo - Domain:
kschappell.com - Service Type:
HTTP - URL:
nginx:80
- Subdomain:
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)
# 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
# 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:
docker compose logs -f
4. Verify
# 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:
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:
curl -I https://dvt-demo.kschappell.com
Should see:
- No
X-Frame-Optionsheader 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
cd /mnt/fast-pool/apps/portfolio-demos/py-dvt-ate/deploy
git pull
docker compose up -d --build
Stopping
docker compose down