# Purpose: put one hardened front door in front of the Part 9 gateway - TLS for the whole # conversation, a per-client request rate limit, a body-size ceiling, and # streaming that is not ruined by response buffering # Platform: all (this is an NGINX server block; on Linux it goes in # /etc/nginx/conf.d/, on macOS under the Homebrew prefix's nginx/servers/) # Minimum memory: 8 GB, which is what the models behind it need; NGINX itself needs almost # nothing # Assumes: the LiteLLM gateway from Part 9 listening on 127.0.0.1:4000, a certificate and # key you already have, and NGINX with the http_ssl_module. NGINX does not read # environment variables from its configuration, so the three values that differ # per machine - the server name and the two certificate paths - are written here # as examples and you edit them once. Nothing secret belongs in this file: the # key is referenced by path, never inlined, and the API keys live in LiteLLM. # --------------------------------------------------------------------------------------- # The rate-limiting zone. NGINX's documentation gives the syntax as # limit_req_zone key zone=name:size rate=rate; # and notes that one megabyte holds about 16,000 of the 64-byte states that # $binary_remote_addr produces. One request per second sounds absurdly low until you # remember that a chat request occupies a model for seconds, and that the burst below is # what absorbs a person typing quickly. This directive belongs in the http context: if # your distribution's nginx.conf includes conf.d/*.conf inside http, it belongs here; if # it includes it at the top level, move this one line into nginx.conf itself. limit_req_zone $binary_remote_addr zone=llmapi:10m rate=2r/s; # Redirect the plain-HTTP port rather than serving on it. A client that talks to port 80 # has already sent its key in clear text, so the redirect is a convenience for humans # typing an address, not a security control. server { listen 80; server_name gateway.home.arpa; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name gateway.home.arpa; # NGINX's HTTPS guide gives this pair as the minimum. The certificate can come from # your own certificate authority for a name that only exists inside the house, or from # a public one for a name that resolves publicly; the directives are the same either # way. Since nginx 1.27.3 the default protocols are TLSv1.2 and TLSv1.3, which is what # you want, so they are not repeated here. ssl_certificate /etc/ssl/local/gateway.crt; ssl_certificate_key /etc/ssl/local/gateway.key; # The documented optimisation for a machine serving more than one client: one megabyte # of this cache holds about 4000 sessions, so handshakes are not repeated all day. ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # A prompt is small. A prompt with a hundred-megabyte "document" attached is somebody # finding out what your machine does when it runs out of memory. Raise this # deliberately if you serve long documents, and know what you raised it to. client_max_body_size 2m; # Nothing about the gateway's internals belongs in a response header. server_tokens off; location / { # burst absorbs a short flurry from one client; requests beyond it are refused # rather than queued, because a queued LLM request is a request that will time out # somewhere less visible. The refusal status is 503 by default, which the # documentation states and which is what your dashboard will show. limit_req zone=llmapi burst=10 nodelay; proxy_pass http://127.0.0.1:4000; # Version 1.1 is the documented default and is what keepalive connections need. proxy_http_version 1.1; # Without these the gateway's log records NGINX as every caller, and the rate # limit you configured is the only place a client's address still exists. 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; # Token streaming is a long response sent slowly. Buffering it means the reader # sees nothing and then sees everything, which reads as a hang. proxy_buffering off; # The documented default read timeout is 60 seconds, which is shorter than a cold # model load. This is the single most common cause of "the gateway works from the # machine itself and times out from anywhere else". proxy_read_timeout 600s; proxy_send_timeout 600s; } # The two cheap health endpoints, exempt from the rate limit so that a monitor polling # every fifteen seconds does not consume a real client's allowance. Neither of them # touches a model and neither of them needs a key, which is exactly why they are the # ones a monitor should poll. location /health/liveliness { proxy_pass http://127.0.0.1:4000; access_log off; } location /health/readiness { proxy_pass http://127.0.0.1:4000; access_log off; } # Prometheus, Grafana, llama-swap's web page and LiteLLM's own /metrics are operator # surfaces. They are not published here, and the stack keeps them on the loopback # address. If you need them from another machine, reach the machine over a VPN rather # than adding a location block to this file. }