Switch Fly proxy to upstream keepalive pools (#337)
All checks were successful
Deploy Fly.io Proxy / deploy (push) Successful in 1m37s
All checks were successful
Deploy Fly.io Proxy / deploy (push) Successful in 1m37s
## Summary - Replace per-request DNS resolution (variable-based `proxy_pass`) with static `upstream` blocks and `keepalive` connection pools - Reuses TLS connections through the Tailscale tunnel instead of handshaking per request - Add `mise run fly-reload` for nginx config reload without full redeploy (re-resolves upstream DNS) ## Trade-off DNS is resolved at config load, not per-request. If Tailscale Ingress pods get new IPs (restart, reschedule), `mise run fly-reload` is needed. A Grafana alert will be added to detect this. ## Still TODO on this branch - [ ] Grafana alert for upstream unreachable (triggers fly-reload reminder) - [ ] Docs pass - [ ] Deploy from branch and verify latency improvement - [ ] Changelog fragment 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #337
This commit is contained in:
parent
54b1cee950
commit
fe0e913963
12 changed files with 229 additions and 102 deletions
|
|
@ -46,18 +46,32 @@ http {
|
|||
proxy_cache_path /tmp/cache levels=1:2 keys_zone=services:10m
|
||||
max_size=200m inactive=24h;
|
||||
|
||||
# MagicDNS resolver — using a variable in proxy_pass defers upstream DNS
|
||||
# resolution to request time (not config time). Results are cached for
|
||||
# 30s per worker to avoid per-request DNS lookups.
|
||||
# WebSocket-aware Connection header. Only send "upgrade" when the client
|
||||
# actually requests a protocol switch; otherwise empty string to preserve
|
||||
# upstream keepalive connections.
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default "";
|
||||
websocket upgrade;
|
||||
}
|
||||
|
||||
# --- Upstream pools with keepalive ---
|
||||
# DNS is resolved once at config load via MagicDNS. If Tailscale Ingress
|
||||
# pods get new IPs (restart, reschedule), run `mise run fly-reload` to
|
||||
# re-resolve. A Grafana alert fires when upstreams are unreachable.
|
||||
resolver 100.100.100.100 valid=30s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# WebSocket-aware Connection header. Only send "upgrade" when the client
|
||||
# actually requests a protocol switch; otherwise "close" (the HTTP/1.1
|
||||
# default when keepalive pooling is not available).
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default close;
|
||||
websocket upgrade;
|
||||
upstream forge_backend {
|
||||
server forge.tail8d86e.ts.net:443;
|
||||
keepalive 8;
|
||||
}
|
||||
upstream docs_backend {
|
||||
server docs.tail8d86e.ts.net:443;
|
||||
keepalive 4;
|
||||
}
|
||||
upstream cv_backend {
|
||||
server cv.tail8d86e.ts.net:443;
|
||||
keepalive 4;
|
||||
}
|
||||
|
||||
# --- docs.eblu.me (static site) ---
|
||||
|
|
@ -76,12 +90,16 @@ http {
|
|||
internal;
|
||||
}
|
||||
location / {
|
||||
set $upstream_docs https://docs.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_docs$request_uri;
|
||||
proxy_pass https://docs_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name docs.tail8d86e.ts.net;
|
||||
proxy_set_header Host docs.tail8d86e.ts.net;
|
||||
proxy_intercept_errors on;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
# Cache aggressively — static site only.
|
||||
# Do NOT use these settings for dynamic services.
|
||||
proxy_cache services;
|
||||
|
|
@ -116,12 +134,16 @@ http {
|
|||
}
|
||||
|
||||
location / {
|
||||
set $upstream_cv https://cv.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_cv$request_uri;
|
||||
proxy_pass https://cv_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name cv.tail8d86e.ts.net;
|
||||
proxy_set_header Host cv.tail8d86e.ts.net;
|
||||
proxy_intercept_errors on;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_cache services;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_valid 404 1m;
|
||||
|
|
@ -187,10 +209,10 @@ http {
|
|||
location ~ ^/user/(login|sign_up|forgot_password) {
|
||||
limit_req zone=forge_auth burst=5 nodelay;
|
||||
|
||||
set $upstream_forge https://forge.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_forge$request_uri;
|
||||
proxy_pass https://forge_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name forge.tail8d86e.ts.net;
|
||||
proxy_intercept_errors on;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
|
|
@ -206,10 +228,13 @@ http {
|
|||
# Cache release artifact downloads — immutable files keyed by tag+filename.
|
||||
# Avoids hammering Forgejo when crawlers or users re-download the same asset.
|
||||
location ~ ^/[^/]+/[^/]+/releases/download/ {
|
||||
set $upstream_forge_releases https://forge.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_forge_releases$request_uri;
|
||||
proxy_pass https://forge_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name forge.tail8d86e.ts.net;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_cache services;
|
||||
proxy_cache_valid 200 7d;
|
||||
|
|
@ -226,10 +251,13 @@ http {
|
|||
|
||||
# Selectively cache static assets only
|
||||
location ~* \.(css|js|png|jpg|svg|woff2?)$ {
|
||||
set $upstream_forge_static https://forge.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_forge_static$request_uri;
|
||||
proxy_pass https://forge_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name forge.tail8d86e.ts.net;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_cache services;
|
||||
proxy_cache_valid 200 7d;
|
||||
|
|
@ -240,10 +268,10 @@ http {
|
|||
}
|
||||
|
||||
location / {
|
||||
set $upstream_forge https://forge.tail8d86e.ts.net;
|
||||
proxy_pass $upstream_forge$request_uri;
|
||||
proxy_pass https://forge_backend$request_uri;
|
||||
proxy_ssl_verify off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name forge.tail8d86e.ts.net;
|
||||
proxy_intercept_errors on;
|
||||
|
||||
# NO proxy_cache — dynamic content with sessions
|
||||
|
|
|
|||
10
fly/start.sh
10
fly/start.sh
|
|
@ -11,10 +11,18 @@ tailscale up --authkey="${TS_AUTHKEY}" --hostname=flyio-proxy
|
|||
until tailscale status > /dev/null 2>&1; do sleep 1; done
|
||||
echo "Tailscale connected"
|
||||
|
||||
# Wait for MagicDNS to be ready — upstream blocks resolve DNS at config
|
||||
# load, so nginx will fail to start if MagicDNS can't resolve yet.
|
||||
echo "Waiting for MagicDNS..."
|
||||
until nslookup forge.tail8d86e.ts.net 100.100.100.100 > /dev/null 2>&1; do
|
||||
sleep 1
|
||||
done
|
||||
echo "MagicDNS ready"
|
||||
|
||||
# Ensure fail2ban deny file exists before nginx starts
|
||||
touch /etc/nginx/forge-deny.conf
|
||||
|
||||
# Start nginx — MagicDNS is available, health check passes immediately.
|
||||
# Start nginx — MagicDNS is available, upstreams resolved.
|
||||
nginx -g "daemon off;" &
|
||||
NGINX_PID=$!
|
||||
echo "Nginx started"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue