Files
2026-07-12 10:17:17 -04:00

96 lines
3.0 KiB
Markdown

# Nginx Runtime DNS Resolution
## Problem
nginx resolves all `proxy_pass` hostnames at **startup time**. If DNS isn't available when nginx starts (common at boot, especially with systemd-resolved still initializing), it fails with:
```
host not found in upstream "api.deepseek.com" in /etc/nginx/sites-enabled/shopproquote:7
```
**One bad upstream takes down every site.** All services become unreachable until someone manually restarts nginx.
## Solution: Runtime DNS via variable
When `proxy_pass` uses a **variable**, nginx resolves at **request time** instead of startup. Three directives needed:
```nginx
location /deepseek/ {
resolver 127.0.0.53 valid=30s; # DNS server + cache TTL
set $deepseek_upstream api.deepseek.com; # variable holds hostname
proxy_pass https://$deepseek_upstream/; # resolves at request time
# ... rest of location block unchanged
}
```
**Key points:**
- `resolver` — use the local DNS stub (`127.0.0.53` on systemd-resolved systems, `8.8.8.8` as fallback). `valid=30s` caches resolved IPs for 30 seconds.
- `set $upstream_name` — the variable name is arbitrary but must be unique per location.
- `proxy_pass https://$upstream_name/` — the trailing `/` is critical (strips the location prefix from the proxied URI, same behavior as static `proxy_pass`).
## Worked example: ShopProQuote DeepSeek proxy
**Before** (static — fails at boot if DNS down):
```nginx
location /deepseek/ {
proxy_pass https://api.deepseek.com/;
proxy_http_version 1.1;
proxy_set_header Host api.deepseek.com;
proxy_set_header Authorization "Bearer sk-f73...b026";
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_server_name on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_read_timeout 120;
}
```
**After** (runtime DNS — safe at boot):
```nginx
location /deepseek/ {
resolver 127.0.0.53 valid=30s;
set $deepseek_upstream api.deepseek.com;
proxy_pass https://$deepseek_upstream/;
proxy_http_version 1.1;
proxy_set_header Host api.deepseek.com;
proxy_set_header Authorization "Bearer sk-f73...b026";
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_server_name on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_read_timeout 120;
}
```
Test: `nginx -t && systemctl reload nginx`
## When to apply
This pattern is needed for **any** `proxy_pass` to an external hostname (not a Tailscale IP or localhost). External hostnames that could be unresolvable at boot:
- API endpoints (`api.deepseek.com`, `api.openai.com`)
- CDN origins
- Third-party service backends
**NOT needed** for:
- Tailscale IPs (`http://100.93.253.36:11434/`) — always resolvable, no DNS involved
- `localhost` / `127.0.0.1`
- IP literals of any kind
## Diagnosing at runtime
```bash
# Is nginx running?
systemctl status nginx
# What error killed it?
journalctl -u nginx --since "5 min ago" --no-pager | grep -i "host not found"
# Does DNS work now?
nslookup api.deepseek.com
# If DNS works now but nginx is down, just restart:
systemctl restart nginx
```