{"id":1409,"date":"2026-10-15T22:33:03","date_gmt":"2026-10-15T22:33:03","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1409"},"modified":"2026-10-15T22:33:03","modified_gmt":"2026-10-15T22:33:03","slug":"tuning-nginx-high-concurrency-vps-worker-keepalive","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/","title":{"rendered":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted folklore. The real limits are arithmetic, not mystique: connections per worker, worker count, file descriptor ceilings, and how long idle keepalive connections hold resources. This article derives the correct values for your specific VPS from measured capacity, then shows how to verify the tuning actually lifted throughput. It pairs naturally with broader <a href=\"https:\/\/virtualserversvps.com\/\">VPS performance work<\/a>; for the related question of process pool sizing, the blog also covers <a href=\"https:\/\/virtualserversvps.com\/blog\/vps-nginx-performance-tuning-caching\/\">Nginx worker_processes and worker_connections sizing<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Arithmetic Behind worker_connections<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Total concurrent connections a server can hold is roughly:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>worker_processes \u00d7 worker_connections \u00f7 2<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The division by two matters: when Nginx proxies a request, it uses one connection to the client <strong>and<\/strong> one to the upstream backend. A reverse-proxy config therefore consumes two connection slots per active request. If you set <code>worker_connections 1024<\/code> on a 2-vCPU box with <code>worker_processes 2<\/code>, your real ceiling is about 1,024 simultaneous proxied requests \u2014 not 2,048. Getting this wrong is the most common source of mysterious capacity limits.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Choosing worker_processes and worker_connections<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Set <code>worker_processes auto;<\/code> to match vCPUs \u2014 check the count first, since a &#8220;2 GB plan&#8221; might still be single-vCPU:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nproc\nnginx -V 2&gt;&amp;1 | tr ' ' '\\n' | grep with-<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then size <code>worker_connections<\/code> generously, because the cost is only the memory of the connection structures, not per-connection processes. A static-file server and a reverse proxy need different values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker_processes auto;\nevents {\n    worker_connections 4096;\n    multi_accept on;\n    use epoll;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>multi_accept on<\/code> lets a worker accept all pending connections in one wakeup, reducing wakeups under bursty load. <code>use epoll<\/code> is the correct event method on Linux \u2014 leave it explicit so it does not silently differ from a vendor build&#8217;s default.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Raising File Descriptor Limits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">None of the above works if the process cannot open enough file descriptors. Every connection is a descriptor, and the default soft limit of 1024 will cap you far below <code>worker_connections<\/code>. Raise both the system limit and the service limit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/security\/limits.conf\nnginx soft nofile 65536\nnginx hard nofile 65536\n\n# \/etc\/systemd\/system\/nginx.service.d\/override.conf\n[Service]\nLimitNOFILE=65536<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reload systemd and restart Nginx, then confirm from inside the running process:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl daemon-reload &amp;&amp; sudo systemctl restart nginx\ncat \/proc\/$(cat \/run\/nginx.pid)\/limits | grep 'open files'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The verified number must match your target. The general topic of descriptor ceilings on busy hosts is covered in <a href=\"https:\/\/virtualserversvps.com\/blog\/file-descriptor-ulimits-vps-high-connection-counts\/\">raising file descriptor and process limits on a VPS<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Keepalive Sizing: The Hidden Connection Consumer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Idle keepalive connections occupy a worker connection slot for the whole timeout. The default <code>keepalive_timeout 75s<\/code> means a client that fetched one asset and vanished still holds a slot for over a minute. Under high concurrency this is a large fraction of your capacity. Tune both sides:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>keepalive_timeout 15s;\nkeepalive_requests 1000;\n\nupstream backend {\n    server 127.0.0.1:8080;\n    keepalive 32;\n    keepalive_requests 1000;\n    keepalive_timeout 60s;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two distinct settings are at play. The client-facing <code>keepalive_timeout<\/code> (15s) frees slots faster from browsers. The upstream <code>keepalive 32<\/code> pool reuses backend connections instead of opening a new one per request, which matters enormously for a reverse-proxy setup. The mechanics of sizing that upstream pool are detailed in <a href=\"https:\/\/virtualserversvps.com\/blog\/nginx-upstream-keepalive-connection-pooling-vps\/\">Nginx upstream keepalive connection pooling<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying the Tuning Lifted Capacity<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not stop at configuration \u2014 measure the difference. Enable the stub status module and watch active connections under load:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>location \/nginx_status {\n    stub_status;\n    allow 127.0.0.1;\n    deny all;\n}\n\ncurl -s http:\/\/127.0.0.1\/nginx_status<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then drive load with a concurrency-capable tool and compare before\/after. <code>wrk<\/code> at a fixed connection count shows whether the tuned config serves more requests per second at the same latency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wrk -t4 -c400 -d30s http:\/\/127.0.0.1\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A properly tuned server shows higher throughput <em>and<\/em> lower tail latency at the same <code>-c400<\/code> connection count. If throughput is flat while compression and caching are already off, the bottleneck is elsewhere \u2014 CPU, upstream, or disk \u2014 and no amount of Nginx tuning will help.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Mistakes to Avoid<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pushing <code>worker_connections<\/code> to absurd values (100000) wastes memory and masks the real limit. Disabling keepalive entirely to &#8220;save connections&#8221; usually makes latency worse by forcing a TCP and TLS handshake on every request. And raising every setting at once means you cannot tell which change helped \u2014 adjust one variable, re-benchmark, then move on.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Timeouts and Buffers That Interact With Concurrency<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beyond connection counts, three directives decide how long a slot stays occupied and how much memory each request holds. They are frequently left at defaults that suit a dedicated server, not a small VPS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>client_body_timeout 12s;\nclient_header_timeout 12s;\nsend_timeout 12s;\n\nclient_body_buffer_size 16k;\nclient_header_buffer_size 1k;\nlarge_client_header_buffers 2 1k;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Shorter timeouts evict slow or abandoned clients faster, returning their connection slots to the pool \u2014 a direct concurrency win. The buffer sizes are a memory-versus-robustness trade: smaller buffers let more simultaneous requests fit in a fixed RAM budget, but headers larger than one buffer spill to disk, adding I\/O latency. On a memory-constrained host, the right answer is usually modest buffers and a higher accepted-headers limit rather than one large buffer per connection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Compute Your RAM Budget Before Raising Limits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every active connection carries kernel socket buffers plus Nginx&#8217;s own per-request structures. Raising <code>worker_connections<\/code> without checking available RAM is how a tuning session turns into an OOM event. Estimate the ceiling from free memory first:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>free -m\ncat \/proc\/meminfo | grep -E 'MemAvailable|Committed_AS'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If peak concurrency would consume more than the available headroom, either cap <code>worker_connections<\/code> below that point or lean harder on fast-cache and file-serving offload so fewer requests reach the backend. The general principle of reading memory pressure correctly is covered in <a href=\"https:\/\/virtualserversvps.com\/blog\/load-average-vs-cpu-saturation-vps-monitoring\/\">reading load average versus CPU saturation<\/a> \u2014 the same false-confidence trap applies to connection counts that look healthy while the machine is already swapping.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Takeaway<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">High-concurrency Nginx tuning is arithmetic, not magic. Divide by two for proxy connections, match <code>worker_connections<\/code> to a raised file descriptor limit, shorten client keepalive while pooling upstream keepalive, and verify every change with stub_status and a load generator. Tuned this way, a modest VPS handles concurrency far beyond its apparent size \u2014 and you will know the real ceiling instead of guessing at it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted folklore. The real limits are arithmetic, not&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":3,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-1409","post","type-post","status-publish","format-standard","hentry","category-performance-optimization"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing\" \/>\n<meta property=\"og:description\" content=\"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-15T22:33:03+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/\",\"name\":\"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-15T22:33:03+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing - Virtual Servers VPS Blog","description":"Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/","og_locale":"en_US","og_type":"article","og_title":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing","og_description":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing","og_url":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-15T22:33:03+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/","url":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/","name":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-15T22:33:03+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Nginx is famous for handling enormous concurrency on modest hardware, but the defaults are conservative and the tuning advice online is mostly copy-pasted","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/tuning-nginx-high-concurrency-vps-worker-keepalive\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tuning Nginx for High-Concurrency VPS Workloads: worker_connections and Keepalive Sizing"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1409"}],"version-history":[{"count":1,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1409\/revisions"}],"predecessor-version":[{"id":1413,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1409\/revisions\/1413"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}