3.0 KiB
3.0 KiB
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:
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.53on systemd-resolved systems,8.8.8.8as fallback).valid=30scaches 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 staticproxy_pass).
Worked example: ShopProQuote DeepSeek proxy
Before (static — fails at boot if DNS down):
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):
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
# 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