Unlocking Multi‑Process Server Speed: How Linux REUSEPORT Works

This article explains why Linux kernels 3.9+ allow multiple processes to bind the same port, how the REUSEPORT option implements kernel‑level load balancing, and provides step‑by‑step examples showing configuration, code snippets, and performance verification.

ITPUB
ITPUB
ITPUB
Unlocking Multi‑Process Server Speed: How Linux REUSEPORT Works

Problem addressed by SO_REUSEPORT

Historically a service could listen on a fixed port (e.g., Nginx on 80, MySQL on 3306). As web traffic grew after 2010, the single‑bind model became a bottleneck for high‑concurrency servers. Two classic multi‑process designs were used:

A dispatcher process accepts new connections and forwards them to worker processes, incurring an extra context switch and creating a dispatcher bottleneck.

Multiple workers share one listening socket and accept directly (as Nginx does), but they must serialize access with a lock, causing lock contention.

Both approaches suffer performance penalties under massive load.

SO_REUSEPORT introduction

Linux 3.9 (2013) introduced the SO_REUSEPORT socket option, allowing several user‑space processes to bind and listen on the same port while the kernel performs load balancing.

Kernel commit references: https://github.com/torvalds/linux/commit/da5e36308d9f7151845018369148201a5d28b46d and https://github.com/torvalds/linux/commit/055dc21a1d1d219608cd4baac7d0683fb2cbbe8

Enabling the option

setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, ...);

This call sets the kernel socket field sk_reuseport to 1.

Bind‑time handling

During inet_bind the kernel calls inet_csk_get_port. If both the existing socket and the new socket have SO_REUSEPORT enabled and belong to the same user namespace, the bind succeeds; otherwise a bind conflict occurs.

if (tb->fastreuseport > 0 && sk->sk_reuseport && uid_eq(tb->fastuid, uid)) {
    goto success; // bind succeeds
} else {
    // bind conflict
}

The uid_eq check ensures that only sockets owned by the same UID can share the port, preventing cross‑user hijacking.

Accept‑time load balancing

When a client connects, the kernel looks up all listening sockets with the same port using __inet_lookup_listener. It computes a score for each socket based on factors such as destination IP match and socket family. The socket with the highest score wins; ties are broken pseudo‑randomly.

score = compute_score(sk, net, hnum, daddr, dif);
if (score > hiscore) {
    result = sk;
    hiscore = score;
    reuseport = sk->sk_reuseport;
    if (reuseport) {
        phash = inet_ehashfn(...);
        matches = 1;
    }
} else if (score == hiscore && reuseport) {
    matches++;
    if (((u64)phash * matches) >> 32 == 0)
        result = sk;
    phash = next_pseudo_random32(phash);
}

The scoring logic prefers sockets bound to the exact destination IP (score 4) over wildcard bindings (score 2). This explains why a process bound to 10.0.0.2:6000 receives connections to that address, while a process bound to 0.0.0.0:6000 handles the rest.

Practical demonstration

Minimal server implementation that enables SO_REUSEPORT is available at: https://github.com/yanfeizhang/coder-kung-fu/blob/main/tests/network/test08/server.c

Running multiple instances on the same port

$ ./test-server 0.0.0.0 6000
Start server on 0.0.0.0:6000 successed, pid is 23179

Running the same command in several terminals shows that all instances start successfully, confirming that the port is shared.

Verifying kernel load balancing

With three identical servers listening on 0.0.0.0:6000, a client that opens many connections receives roughly equal numbers of connections on each server, demonstrating random distribution.

Server 0.0.0.0 6000 (23179) accept success:15
Server 0.0.0.0 6000 (23177) accept success:25
Server 0.0.0.0 6000 (23185) accept success:20

Priority matching test

On a machine with two IPs (10.0.0.2 and 10.0.0.3), start:

A: ./test-server 10.0.0.2 6000
B: ./test-server 0.0.0.0 6000

Connecting to 10.0.0.2 hits process A (score 4). Connecting to 10.0.0.3 falls back to process B (score 2).

Cross‑user security check

If a server is started by user alice with SO_REUSEPORT, a second user (e.g., root ) attempting to bind the same port receives “Bind Failed!” because the UID check blocks cross‑user reuse.

Summary

Before Linux 3.9 a port could be bound by only one socket, limiting multi‑process server scalability. The REUSEPORT feature introduced in 3.9 lets multiple processes bind the same port, and the kernel performs random or score‑based load balancing, eliminating dispatcher bottlenecks and lock contention.

In Nginx 1.9.1+ the feature can be enabled with a single line:

server {
    listen 80 reuseport;
    ...
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendload balancingLinuxSocketSO_REUSEPORT
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.