# 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
```
