Without keepalive on Nginx upstream blocks, every proxied request opens a new TCP connection to your backend. One directive fixes this.
By default, Nginx closes the connection to your upstream server after each request. For every proxied request: TCP handshake → request → response → TCP teardown. On internal services (same datacenter, low latency), this is still 1–2ms of overhead per request. At 1000 req/s, that's 1–2 seconds of pure TCP overhead per second.
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32; # maintain up to 32 idle connections to the backend
}
server {
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1; # required for keepalive
proxy_set_header Connection ""; # clear the Connection: close header
}
}keepalive 32 tells Nginx to keep up to 32 idle connections open to the upstream. proxy_http_version 1.1 is required — HTTP/1.0 doesn't support keepalive. Connection "" clears the header that would otherwise signal connection closure.
On the Prachyam Nextcloud setup, adding upstream keepalive dropped median response time on the WebDAV API from ~18ms to ~11ms — a 38% reduction just from not doing the TCP handshake on every request.
For a low-traffic portfolio it's marginal. For any service handling real concurrency, it's meaningful.
Karanveer Singh Shaktawat
Full Stack Engineer & Infrastructure Architect
Building portfolio, contributing to open source, and seeking remote full-time roles with significant technical ownership.
Pick what you want to hear about — I'll only email when it's worth it.
Did this resonate?
One changed line in a Dockerfile invalidates every layer after it. Ordering your Dockerfile with this in mind cuts rebuild times dramatically.
How Nextcloud handles large file uploads under the hood, and how to implement chunked uploads manually when the desktop client isn't an option.
# Watch Nginx's upstream connections
watch -n1 'ss -tnp | grep :3000'
# Without keepalive: connections appear and disappear on every request
# With keepalive: you see persistent ESTABLISHED connections between requestsMore idle connections held open = more file descriptors. For a small internal service, keepalive 8–16 is plenty. Don't set it to 1000 — you'll exhaust file descriptors before you benefit.