Nginx Reverse Proxy
Four things every engineer 'knows' about nginx, and what 1.30 actually does. worker_processes times worker_connections is a SLOT count, not a client count: ngx_event_connect_peer() draws the upstream socket from the same per-worker array the listener draws an accepted client from, so an in-flight proxied request holds two slots, an idle cached upstream connection holds one, and the listener and channel take one each before any request arrives — which is why a four-worker, 1024-connection box tops out near two thousand concurrent proxied clients rather than four thousand, and why the error log says 'worker_connections are not enough' about a backend that was healthy the whole time. worker_processes auto is not container-aware: it is two lines of source that resolve to the host's online CPU count via sysconf(_SC_NPROCESSORS_ONLN), reading neither the cgroup quota nor the process's affinity mask, so half a core on a 64-core node still forks 64 workers, each with its own connection array — Envoy, asked the same question, takes the minimum of hardware threads, affinity and the cgroup CPU limit, and runs its workers as threads in one process rather than as forked children. The back-pressure rule every tuning post quotes has two consumers and neither one stops a worker accepting: the famous one — decrement the counter, skip the accept mutex this round — is only consulted while accept_mutex is on, and that has defaulted to off since EPOLLEXCLUSIVE made it unnecessary; the one that does run re-arms the listening socket the moment the worker passes seven eighths, instead of on every sixteenth accept, so another worker gets the next wakeup. Neither frees a slot. What actually closes connections is a different watermark in a different file — below a sixteenth of the array free, nginx closes idle REUSABLE connections in batches of up to thirty-two, and reusable means client keepalive, never the upstream cache, so under slot pressure your clients pay the reconnect and the TLS handshake while your backends stay warm. Upstream keepalive itself is now the default for an explicit upstream block, thirty-two connections scoped local, with HTTP/1.1 to the backend and no Connection header as defaults too, so the three-line recipe from the old runbooks describes what you already have — and a cache one connection too small does not degrade into partial reuse, it closes the oldest cached connection on every release to store the one just freed. The two shared-memory zones are sized in entries, not bytes, and the slab allocator rounds every small allocation to a power of two: a cached object's index node is 120 bytes in a 128-byte class, a rate-limiter key is 84 bytes in the same 128-byte class, so a limiter zone holds about half what its struct size suggests and a keys_zone plateaus at tens of thousands of objects with terabytes of disk still free — while eviction runs on inactivity rather than TTL, so a long max-age buys nothing for a long tail. The limiter has to live in shared memory for a reason worth internalising beyond nginx: the kernel spreads one client's connections across workers, so a per-worker counter enforces the configured rate once per worker. And the arithmetic that ties the whole module together is a queueing identity — an admitted rate times an upstream timeout is a concurrency, denominated in exactly those two-per-request slots, so a limiter set above that line does not protect the proxy; it only decides which request fails. Change worker_processes, worker_connections, upstream keepalive, proxy_cache, the limit_req rate, the upstream timeout and the TLS session cache, and watch the server-computed cascade across all five layers — event loop, upstream connections, caching, TLS termination and rate limiting — including the conjunctions where two settings that are each survivable alone are not survivable together. Validated against nginx release-1.30.5 source (ngx_set_worker_processes, ngx_os_init, ngx_event_process_init, ngx_event_connections, ngx_event_accept, ngx_event_connect_peer, ngx_get_connection, ngx_drain_connections, ngx_slab_alloc_locked, ngx_http_file_cache_exists, ngx_http_file_cache_set_slot, ngx_http_upstream_keepalive_init_main_conf, ngx_http_upstream_free_keepalive_peer, ngx_http_limit_req_zone, ngx_http_limit_req_lookup, ngx_ssl_session_cache, ngx_ssl_new_session), with Envoy v1.39.1 cited only where the architectures genuinely differ.
- All current + future modules
- Full kata library
- Progress dashboard + weak area analysis
Cancel anytime. Billed annually.