Who ends up in TIME_WAIT
TIME_WAIT belongs to the side that initiates the close — the one that sends the first
FIN. Not the server, not the client, by role. By who hangs up.
That single fact determines which machine suffers:
- The server closes (typical for HTTP without keep-alive, or with a short idle
timeout). The server accumulates
TIME_WAITentries, one per connection, each holding its 4-tuple for 2×MSL — 60 seconds on Linux, and not tunable without a rebuild. At a few thousand connections per second, that is hundreds of thousands of entries. - The client closes. The client accumulates them, and because the client side
picks an ephemeral port per connection, it is the one that runs out. The default
net.ipv4.ip_local_port_rangegives roughly 28,000 ports; sustained connections to a single destination will exhaust them long before anything else breaks.
Why the state exists
Two reasons, and neither is negotiable. It absorbs a retransmitted final FIN so the peer's close completes cleanly, and it prevents a delayed segment from the old connection being delivered into a new connection reusing the same 4-tuple. Removing it does not make the problem go away; it makes the problem silent and rare, which is worse.
What actually helps
Keep-alive, so there are fewer closes to begin with. That is the whole answer, and most of the tuning advice that circulates is a way of avoiding it.
SO_REUSEADDR lets a server rebind its listening socket over lingering entries, which
solves restart friction and nothing else. net.ipv4.tcp_tw_reuse lets the
connecting side reuse entries when timestamps make it safe, which helps a client with port
exhaustion. tcp_tw_recycle is not an option: it broke clients behind NAT and was
removed from Linux in 4.12.