PowerForecast Docs
Back to Wizard
This page belongs to the full documentation. You don't need it to plan: back to the essentials.

Installation on a Hetzner server

PowerForecast is two deployables: the engine (forecast-api, a container with its Redis sidecar) and the wizard (forecast-wizard, static files). Sign-in, the wizard template and AI assistance come from PowerAdmin (admin-api.planningpowertools.com); PowerForecast has no database of its own.

For the base server (Alpine Linux, SSH, Docker, Nginx, certbot), follow Parts 1 – 3 of PowerSolver's documentation/installation/HETZNER_GUIDE.md.

Architecture

https://forecast.planningpowertools.com        Nginx → /var/www/powerforecast   (forecast-wizard + Documentation/html)
https://forecast-api.planningpowertools.com    Nginx → localhost:8020            (forecast-api)
                                                         ├── Redis (powerforecast-redis, compose network): running jobs
                                                         └── volume powerforecast-data: stored forecasts (SQLite)
ServicePortPublic URL
forecast-api8020https://forecast-api.planningpowertools.com
Redis6379 inside the compose network (6380 on the host, keep it closed)none
forecast-wizardstatichttps://forecast.planningpowertools.com

1. The engine

Build on the development machine, copy, load:

cd D:\PowerProjects\PowerForecast\forecast-api
docker build -t powerforecast-api:latest .
docker save powerforecast-api:latest -o powerforecast-api.tar
scp powerforecast-api.tar root@YOUR_SERVER_IP:/root/
docker load -i /root/powerforecast-api.tar && rm /root/powerforecast-api.tar
mkdir -p /root/powerforecast && cd /root/powerforecast

Copy forecast-api/docker-compose.yml from the repository into /root/powerforecast/. It starts the engine (powerforecast-api, port 8020, volume powerforecast-data for the stored forecasts) and Redis (powerforecast-redis, volume powerforecast-redis-data):

VariableDefaultMeaning
PORT8020engine port
REDIS_URLredis://redis:6379/0job store
RUN_TTL_SECONDS3600how long finished jobs stay in Redis (the stored run stays for good)
FORECAST_DB_PATH/data/powerforecast.dbthe stored forecasts (SQLite)
ORCHESTRATOR_URLemptyPowerOrchestrator, to fetch expired jobs
FORECAST_N_JOBS1StatsForecast parallelism (keep 1 in Docker)
CORS_ORIGINS*allowed origins
NIXTLA_ID_AS_COL1StatsForecast output format
docker-compose up -d
docker-compose ps
curl http://localhost:8020/health               # {"status":"healthy",…}
docker exec powerforecast-redis redis-cli ping  # PONG

Updating the engine keeps the stored forecasts (volume powerforecast-data). The first start of the release with stored forecasts moves the finished jobs still in Redis into the database; older runs had already expired, and the moved ones have no input history (Results shows them without their past).

Backups (SQLite online backup; a plain copy of a live database can be inconsistent):

mkdir -p /root/backups
docker run --rm -v powerforecast-data:/data -v /root/backups:/backup alpine sh -c \
  "apk add --no-cache sqlite >/dev/null && sqlite3 /data/powerforecast.db \".backup '/backup/powerforecast-$(date +%Y%m%d-%H%M%S).db'\""

No sign-in check on the API yet. Anyone who can reach https://forecast-api.planningpowertools.com can list, change and remove stored forecasts. Until the product has its own authentication, keep regular backups.

Memory: plan about 1 GB for the engine. A forecast of many series runs for minutes (see the Developer Guide, Speed).

2. The wizard and the documentation

The wizard is static. Copy forecast-wizard/ to /var/www/powerforecast/ and Documentation/html/ to /var/www/powerforecast/documentation/html/ (the sidebar's Documentation link opens /documentation/html/index.html). Check forecast-wizard/config.json:

{ "engineApiUrl": "https://forecast-api.planningpowertools.com/api/powerforecast/v1",
  "sqliteApiUrl": "https://admin-api.planningpowertools.com/api" }

3. Nginx and HTTPS

Point the two DNS names (A records) at the server before asking for certificates.

/etc/nginx/http.d/forecast-api.conf:

server {
    listen 80;
    server_name forecast-api.planningpowertools.com;
    location /.well-known/acme-challenge/ { root /var/www/certbot; }
    location / {
        proxy_pass http://localhost:8020;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 600s;
        proxy_send_timeout 600s;
        proxy_read_timeout 600s;     # POST /forecast on many series is long; the wizard uses /jobs
    }
}

/etc/nginx/http.d/forecast.conf:

server {
    listen 80;
    server_name forecast.planningpowertools.com;
    root /var/www/powerforecast;
    index index.html;
    location /.well-known/acme-challenge/ { root /var/www/certbot; }
    location / { try_files $uri $uri/ =404; }
}
mkdir -p /var/www/certbot
nginx -t && service nginx reload
certbot --nginx -d forecast-api.planningpowertools.com -d forecast.planningpowertools.com

Certbot adds the HTTPS blocks and the redirects. Renewal: certbot renew --dry-run once, then a daily cron (0 3 * * * certbot renew --quiet --deploy-hook "service nginx reload").

CORS is answered by the engine (CORS_ORIGINS); do not add CORS headers in Nginx as well, or browsers see them twice and refuse.

4. Firewall

Open 22, 80 and 443 only (Hetzner Cloud Firewall or iptables). 8020 and 6380 stay closed: Nginx reaches the engine on localhost.

5. Checks after an install or an update

curl https://forecast-api.planningpowertools.com/health
curl -s https://forecast-api.planningpowertools.com/api/powerforecast/v1/models | head -c 300

Then in a browser: sign in at https://forecast.planningpowertools.com, load forecast-demos/01-retail-demand-monthly/data.csv on step 2, run, and open Results.

Troubleshooting

SymptomLook at
The wizard says the template cannot be loadedPowerAdmin reachable? sqliteApiUrl in config.json
Run Forecast fails at onceengineApiUrl; curl …/health; docker-compose logs forecast-api
A job stays Forecasting for very longmany series: expected (minutes); docker stats for CPU / memory
Job not found on Results after a whilean engine older than stored forecasts: redeploy; otherwise open the run from Forecasts
Forecasts page: "does not keep forecasts yet"the engine image predates stored forecasts: rebuild and redeploy forecast-api
CORS error in the browserCORS headers added twice (Nginx and engine), or a wrong origin
Container restartsdocker-compose logs; memory (dmesg | grep -i kill)

Quick reference

cd /root/powerforecast
docker-compose ps | logs -f forecast-api | restart forecast-api
docker-compose down && docker-compose up -d                       # after loading a new image
docker exec powerforecast-redis redis-cli --scan --pattern 'powerforecast:job:*' | head