ops: add nginx site config to repo as source-of-truth
The live host nginx config (/etc/nginx/sites-enabled/overlord) was not tracked in git, leading to drift. This commit checks in a source-of-truth copy under nginx/overlord.conf with a deploy procedure documented at the top of the file. Includes the proxy_read_timeout/proxy_send_timeout 1d settings for both WebSocket location blocks (/websocket/ and /). Without these, nginx's default 60s timeout drops idle plugin connections in a reconnect loop — the symptom users saw was "WebSocket error … State: Aborted" every ~60s on idle characters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3c634adbdc
commit
f111e5063b
1 changed files with 97 additions and 0 deletions
97
nginx/overlord.conf
Normal file
97
nginx/overlord.conf
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Nginx site config for overlord.snakedesert.se
|
||||
#
|
||||
# Lives on the host (not in the Docker stack) at:
|
||||
# /etc/nginx/sites-enabled/overlord
|
||||
#
|
||||
# This file is the source-of-truth copy committed to git. To deploy a change:
|
||||
# 1. Edit this file in the repo
|
||||
# 2. SSH to the host
|
||||
# 3. sudo cp /home/erik/MosswartOverlord/nginx/overlord.conf /etc/nginx/sites-enabled/overlord
|
||||
# 4. sudo nginx -t && sudo nginx -s reload
|
||||
#
|
||||
# Critical settings:
|
||||
# - proxy_read_timeout / proxy_send_timeout 1d on /websocket/ and /
|
||||
# WebSockets are long-lived; nginx's default 60s timeout drops idle clients.
|
||||
# Removing these timeouts caused all plugin connections to drop every
|
||||
# ~60s when no data flowed from backend to client (April 2026 incident).
|
||||
# - Bearer token in /grafana/ proxy_set_header is a Grafana service account
|
||||
# token used for anonymous panel embeds. Rotate when credentials leak.
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name overlord.snakedesert.se;
|
||||
|
||||
# Security hardening
|
||||
server_tokens off;
|
||||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
|
||||
|
||||
# SSL certificates
|
||||
ssl_certificate /etc/letsencrypt/live/overlord.snakedesert.se/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/overlord.snakedesert.se/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
# Plugin WebSocket ingest — `/ws/position` upstream
|
||||
location /websocket/ {
|
||||
proxy_pass http://tracker/ws/position;
|
||||
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_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header X-Plugin-Secret $http_x_plugin_secret;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
# Long-lived WebSocket: don't time out the proxy
|
||||
proxy_read_timeout 1d;
|
||||
proxy_send_timeout 1d;
|
||||
}
|
||||
|
||||
# API endpoints (live, trails, history, stats) — short-lived HTTP
|
||||
location /api/ {
|
||||
proxy_pass http://tracker/;
|
||||
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_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# Frontend UI and browser WebSocket (`/ws/live` upstream)
|
||||
location / {
|
||||
proxy_pass http://tracker/;
|
||||
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_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
# Long-lived browser WebSocket (/ws/live): don't time out
|
||||
proxy_read_timeout 1d;
|
||||
proxy_send_timeout 1d;
|
||||
}
|
||||
|
||||
# Grafana Dashboard UI (served under /grafana)
|
||||
location /grafana/ {
|
||||
proxy_pass http://grafana;
|
||||
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_set_header Authorization "Bearer glsa_AcDTcN5CUX9h5Bi2ipmVAs6g1FRTSIWk_8b81cf99";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue