Target platform: DreamHost VPS/Dedicated • Runtime: Python 3 + Gunicorn • Reverse proxy: Apache (.htaccess)
# 0) SSH in as the deploy user (e.g., birdseye)
ssh USER@your-domain
# 1) Create project + venv
mkdir -p ~/burn-tool && cd ~/burn-tool
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install flask gunicorn requests
# 2) Add app.py (and optional windCompass.png), then test:
python app.py
# Visit http://127.0.0.1:8000/ via SSH tunnel if needed (Ctrl+C to stop)
# 3) Gunicorn as a user service
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/gunicorn-burn.service
# (paste the service from the section below), then:
systemctl --user daemon-reload
systemctl --user enable gunicorn-burn
systemctl --user start gunicorn-burn
journalctl --user -u gunicorn-burn -f
# 4) Apache proxy for https://your-domain/planningTool/
# Create .htaccess with rewrite rules (see section below)
# 5) Verify
curl http://127.0.0.1:8020/health
pip
available (python3 --version
).systemd
user services enabled (systemctl --user
works).https://your-domain.com/planningTool/
).If python3 -m venv
is missing ensurepip, install the OS package or use a managed Python from your provider.
mkdir -p ~/burn-tool
cd ~/burn-tool
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install flask gunicorn requests
Copy your working app.py
into ~/burn-tool/
. If you show an icon, place windCompass.png
here as well.
# Optional: quick syntax check
python3 -m py_compile app.py
# Optional: run Flask dev server to sanity-check
python app.py # then Ctrl+C to stop
Create ~/.config/systemd/user/gunicorn-burn.service
:
[Unit]
Description=Gunicorn for Burn Tool
After=network.target
[Service]
User=%u
WorkingDirectory=/home/%u/burn-tool
ExecStart=/home/%u/burn-tool/venv/bin/gunicorn -b 127.0.0.1:8020 app:app
Restart=always
[Install]
WantedBy=default.target
Enable and start:
systemctl --user daemon-reload
systemctl --user enable gunicorn-burn
systemctl --user start gunicorn-burn
systemctl --user status gunicorn-burn --no-pager
journalctl --user -u gunicorn-burn -f
%u
isn’t expanded on your system, replace
/home/%u
with your actual home path (e.g., /home/birdseye
) in both
WorkingDirectory
and ExecStart
.
To serve the app at https://your-domain.com/planningTool/
, create a .htaccess
with:
RewriteEngine On
RewriteBase /planningTool/
RewriteRule ^(.*)$ http://127.0.0.1:8020/$1 [P,L]
RewriteBase
if you use a different path.# Local (Gunicorn)
curl http://127.0.0.1:8020/health
# → {"ok": true}
# Public (through Apache)
https://your-domain.com/planningTool/health
Systemd will restart on crash, but a watchdog ensures recovery if anything external kills the process.
crontab -e
Add:
* * * * * systemctl --user is-active --quiet gunicorn-burn || systemctl --user restart gunicorn-burn
cd ~/burn-tool
# edit app.py or pull from git
systemctl --user restart gunicorn-burn
Symptom | What to check |
---|---|
Port already in use | Change -b 127.0.0.1:8020 to a free port in the service; restart service. |
“Enter a valid value” on Lat/Lon | Inputs must use type="number" step="any" min/max to accept decimals. |
Non-JSON error in UI | Usually Apache returning HTML (404/500). Verify .htaccess path and that Gunicorn is up. |
404 at /forecast |
Ensure proxy path matches app routes (/planningTool/forecast and /forecast are supported). Check rewrite base. |
Indentation / Tab errors | Normalize whitespace and check via python3 -m py_compile app.py . Prefer spaces (4) over tabs. |
Sky cover warnings | Network or 4xx from NWS grid. App retries unfiltered and continues without sky if needed. |
© 2025 • Weather Activity Planning Tool. This guide targets DreamHost; concepts apply to any Apache + Gunicorn stack.