SO_REUSEPORT permits multiple Linux AF_INET or AF_INET6 sockets to bind the same local address and port when every member satisfies the reuse-port rules. For TCP listeners, this moves incoming connection distribution ahead of accept(): the kernel selects a listener from the reuse-port group, and that listener receives the connection on its accept queue. For UDP, selection determines which socket receives an incoming datagram.
This is a different concurrency boundary from several threads sharing one listening file description. Each reuse-port member is a distinct socket, with its own descriptor, queues, polling state, and lifecycle.
Membership starts before bind
SO_REUSEPORT must be enabled on each participating socket before bind(). Linux also requires processes binding the same address into a reuse-port group to have the same effective UID, a restriction intended to prevent unrelated processes from taking traffic for an existing endpoint.
A TCP worker can therefore own a complete listener lifecycle:
int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
listen(fd, backlog);Repeating that sequence for several workers produces several listener sockets bound to the same endpoint. The kernel treats those sockets as a reuse-port group rather than rejecting the later binds as address conflicts.
The option is not interchangeable with SO_REUSEADDR. The latter changes address-reuse checks around bind(); SO_REUSEPORT explicitly supports multiple sockets concurrently bound to an identical address for traffic distribution.
Selection precedes application scheduling
With a single listener shared by many workers, concurrency begins after readiness or after an accept() attempt. Multiple workers can contend on the same listening socket, and mechanisms such as EPOLLEXCLUSIVE can reduce redundant wakeups without creating separate accept queues.
A reuse-port group shifts that boundary. Incoming traffic first encounters kernel socket selection:
packet or connection
|
v
reuse-port selection
/ | \
v v v
L0 L1 L2
| | |
W0 W1 W2For TCP, the selected listening socket becomes the endpoint whose queue receives the new connection. A worker blocked or polling on another listener does not compete to accept that same queued connection. This separation can reduce contention in server designs where each worker owns one listener and one event loop.
The mechanism does not promise equal work at the application layer. Connections can have very different lifetimes, request rates, CPU costs, or response sizes. Even balanced connection assignment can produce uneven worker utilization.
Default distribution is kernel policy
Plain SO_REUSEPORT leaves selection to the kernel’s reuse-port logic. Applications should treat the exact distribution algorithm as Linux implementation behavior rather than a portable API guarantee. The stable interface guarantee is that eligible sockets may share the endpoint and that incoming traffic is assigned among the group according to the kernel’s selection path.
That distinction matters when an application depends on affinity. A design that requires a specific flow class, CPU locality, or hardware receive-queue relationship cannot safely infer such placement from the existence of SO_REUSEPORT alone.
Linux exposes explicit BPF hooks for applications that need programmable selection. SO_ATTACH_REUSEPORT_CBPF and SO_ATTACH_REUSEPORT_EBPF attach a program to a reuse-port group. Classic and socket-filter-style extended programs select an index from 0 through N-1; an invalid result falls back to normal reuse-port selection. BPF_PROG_TYPE_SK_REUSEPORT can use bpf_sk_select_reuseport() for socket choice.
Group indices are operational state
Reuse-port BPF selection uses group membership, so socket indices are not durable application identities. Linux numbers UDP members in bind() order and TCP members in listen() order. When a member closes, the kernel can move the final socket in the group into the removed member’s slot.
That behavior creates an important boundary for BPF policy. A program that maps traffic to numeric positions must account for group changes rather than assuming that index 2 permanently names one worker.
New sockets joining a reuse-port group inherit the group’s attached BPF program. The program can also be replaced by setting the reuse-port BPF option again on a group member. The selection policy is therefore group state, not merely configuration attached independently to each worker.
TCP and UDP expose different units of assignment
For TCP, reuse-port selection occurs in the path that chooses a listening socket for an incoming connection. Once established, the resulting connected socket belongs to that accepted connection; later TCP segments are not independently sprayed across listeners.
For UDP, there is no accept-created connected socket required for normal datagram reception. The selection path chooses a reuse-port member for incoming packets. This makes packet steering policy directly visible to receive-side worker placement.
The difference is structural. A TCP server commonly reasons about connection ownership after listener selection. A UDP service may need to reason about flow consistency and packet placement directly in the reuse-port policy.
Queue ownership changes failure behavior
Separate listeners also mean separate queue state. If one TCP worker stops draining its listener while the process remains alive, connections already directed to that listener are not automatically transformed into pending connections on another listener merely because another worker has spare capacity.
This property makes worker health part of listener management. A server that uses one reuse-port listener per worker needs a lifecycle strategy for removing unhealthy members, closing listeners during shutdown, and coordinating replacement workers. Kernel distribution removes a shared accept point; it does not create an application-level work-stealing system.
The same separation affects observability. Aggregate endpoint traffic can look healthy while one member accumulates pressure. Per-worker accept rates, queue pressure, connection duration, and processing latency provide a more accurate view than a single process-wide connection counter.
Reuse-port and wakeup control solve different problems
SO_REUSEPORT and EPOLLEXCLUSIVE can both appear in high-concurrency network servers, but they act at different layers. EPOLLEXCLUSIVE limits wakeups when multiple epoll instances monitor a shared target. SO_REUSEPORT creates multiple socket targets and lets the networking stack choose among them before user-space acceptance or reception.
The distinction can be summarized as ownership versus notification. Reuse-port changes which socket owns incoming work. Exclusive epoll registration changes which waiters are notified about readiness on a socket. Combining or choosing between them depends on the server’s queue topology, event-loop ownership, affinity requirements, and worker lifecycle.
SO_REUSEPORT is therefore more than a bind convenience. It relocates a concurrency decision into socket lookup and selection, creating independent listener or receive queues whose distribution policy can remain kernel-defined or become explicitly programmable with BPF.
References
- Linux
socket(7):SO_REUSEPORT,SO_ATTACH_REUSEPORT_CBPF, andSO_ATTACH_REUSEPORT_EBPF. - Linux kernel NAPI documentation: reuse-port BPF as a mechanism for receive-side worker placement.