Observability
The portals container is a static-asset server (Nginx) plus a startup entrypoint and a set of
reverse proxies. Observability is therefore mostly about container health, Nginx logs and the
stub_status endpoint; application behaviour is observed in the browser and, for data operations,
in the UI Backend.
Container health
The Compose healthcheck curls a portal path inside the container. A healthy status means Nginx is
serving the apps:
NAME STATUS PORTS
portals healthy 0.0.0.0:8880->80/tcp, 0.0.0.0:8843->443/tcp
healthcheck:
test: [ "CMD", "curl", "-f", "http://127.0.0.1/support-portal/index.html" ]
interval: 10s
timeout: 5s
retries: 50
Metrics
/nginx_status exposes Nginx stub_status for the Prometheus nginx-exporter. Access is restricted
to 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12 and the CIDR in METRICS_ALLOW_IP (default
0.0.0.0/0 — narrow it, see Security Notes).
curl http://<host>:8880/nginx_status
Logs
# Container (entrypoint + Nginx)
docker compose logs -f portals
# Nginx access / error logs on the host, when /var/log/nginx is mounted.
# DATA_FOLDER is set in .env for Compose, not in your shell -- use the install root itself.
tail -f /usr/local/ft-system/portals/nginx/logs/access.log
tail -f /usr/local/ft-system/portals/nginx/logs/error.log
The entrypoint logs the rendered upstreams, the runtime-config patch and the permissions upload — grep those lines to confirm startup succeeded:
docker compose logs portals | grep -iE "generated|patched|upload|WARN"
Browser-side
For data-operation issues, inspect the browser DevTools Network/Console and correlate with the UI
Backend logs. Because every API call now travels through the portals origin, a failing request
shows up as a 502/504 from the /iot-webservice/ proxy when the backend is the problem, and in
the Nginx error log on the host.