Back to articles
Technology Insight

Deep Linux Kernel Tuning: Scaling Node.js VPS to 100,000 Concurrent WebSockets

May 30, 2026

Introduction: The 100k Concurrent Connection Challenge

In modern real-time architecture, maintaining high availability and low latency under heavy loads is a core engineering requirement. Node.js, with its asynchronous, event-driven, non-blocking I/O model, is inherently well-suited for handling a massive number of concurrent connections, such as WebSockets. However, when deployed on a standard Virtual Private Server (VPS), you will quickly hit a wall long before reaching your hardware's actual limits. By default, Linux kernels are tuned for general-purpose server workloads, imposing restrictive boundaries on file descriptors, memory allocation, and networking queues to prevent a single process from consuming all system resources.

To scale a Node.js application to 100,000 concurrent WebSocket connections (100k C10K), you must peer beneath the application layer and optimize the underlying Linux kernel. This comprehensive guide walks through the exact sysctl parameters, security limits, and networking configurations required to unlock your VPS's hidden potential.

Understanding the Architectural Bottlenecks

Every WebSocket connection begins as a standard HTTP handshake that upgrades to a persistent, bi-directional TCP connection. To keep 100,000 of these connections open simultaneously, your operating system must manage three primary constraints:

  • File Descriptors (FDs): In Linux, everything is a file. Each open network socket requires a file descriptor. Default system limits usually restrict this to 1,024 per process, which will crash your Node.js application almost instantly under load.
  • Networking Stack & Queues: The Linux TCP/IP stack buffers incoming connections. If these buffers are too small, packets are dropped, leading to connection timeouts and high latency.
  • Memory Allocation: Each TCP socket requires read and write buffers. If configured too generously, 100k connections will trigger the Out-Of-Memory (OOM) killer. If too restrictive, data throughput stalls.
---

Step 1: Expanding System and Process File Descriptor Limits

Before modifying kernel parameters, we must instruct Linux to allow the allocation of hundreds of thousands of files simultaneously. We need to adjust both system-wide limits and user-space limits.

System-Wide Configuration

Open /etc/sysctl.conf and add the following line to increase the maximum number of file descriptors the entire operating system can open:

fs.file-max = 2097152

This configures the system to support up to ~2 million open files, leaving plenty of overhead for system processes alongside your 100k Node.js connections.

User-Space and Service Configuration

Next, we modify /etc/security/limits.conf to allow the specific user running the Node.js application to open enough files. Assuming your application runs under a user named nodeuser, append:nodeuser soft nofile 150000 nodeuser hard nofile 150000

Note: If you are managing your Node.js application via systemd (e.g., PM2 or a custom service file), these security limits might be overridden. You must explicitly add LimitNOFILE=150000 inside your service's [Service] block to ensure the limits are inherited correctly at runtime.
---

Step 2: Optimizing the TCP IP Stack for Massive Concurrency

With file descriptors solved, the next critical phase is adjusting how the Linux kernel handles TCP state transitions and backlogs. This is done by modifying /etc/sysctl.conf.

Tuning the Connection Backlog

When thousands of clients attempt to connect simultaneously, they enter the SYN backlog queue before the application can accept them. If this queue is full, connections fail.

net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

By raising somaxconn (the maximum socket backlog) and tcp_max_syn_backlog, we protect the system against spikes in connection requests and mitigate basic SYN flood DDoS behaviors.

Accelerating Timewait Reuse

In high-traffic environments, sockets frequently enter the TIME_WAIT state after closing. To prevent the system from running out of available ephemeral ports, enable aggressive port reuse:

net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65535

Expanding the ip_local_port_range provides over 64,000 local ports per local IP, which is vital if your VPS acts as a reverse proxy or establishes outbound microservice connections.

---

Step 3: Calculating and Tuning TCP Memory Buffers

Memory management is where most 100k WebSocket implementations fail. If the kernel allocates too much memory per socket, your VPS will experience RAM exhaustion. If it allocates too little, performance degrades.

The kernel manages TCP window sizes automatically via tcp_rmem (read) and tcp_wmem (write) vectors, which consist of three values: [min, default, max] in bytes. Add the following to /etc/sysctl.conf:

net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

To optimize for 100,000 connections, we want the default size to be relatively small (e.g., 87KB for read, 65KB for write) because WebSockets are often used for small, transactional real-time payloads. If a connection requires more bandwidth, Linux will dynamically scale it up to the 16MB maximum.

Additionally, configure the global TCP memory pages limits:

net.ipv4.tcp_mem = 786432 1048576 1572864

These values define the thresholds (measured in pages, usually 4KB) where the kernel enters pressure mode and starts aggressively cutting down TCP memory consumption.

---

Step 4: Applying Changes and Verification

Once all modifications are written to /etc/sysctl.conf, apply them instantly without restarting the server by running:

sudo sysctl -p

To verify that your Node.js process can actually leverage these new boundaries, you can inspect the limits of your running process using:

cat /proc/[PID]/limits | grep "Max open files"

Conclusion: Monitoring Your High-Scale Infrastructure

Tuning the Linux kernel is a prerequisite for achieving 100,000 concurrent WebSockets, but it is only half the battle. Your Node.js application must also be written efficiently—leveraging lightweight libraries like uWebSockets.js instead of standard ws if you are severely constrained by memory. Furthermore, always ensure you have structured monitoring (using tools like Prometheus and Grafana) tracking your server's context switching, CPU interrupts, and established network states. With these systems properly aligned, your VPS will easily handle enterprise-grade real-time traffic workloads without breaking a sweat.

Deep Linux Kernel Tuning: Scaling Node.js VPS to 100,000 Concurrent WebSockets | DPTCloud