Overriding the Default Linux Kernel 20-Second TCP Socket Connect Timeout
Whatever language or client library you’re using, you should be able to set timeouts on network socket operations — typically split into a connect timeout, a read timeout, and a write timeout.
However, although you can usually set these as small as you like, the connect timeout in particular has an effective maximum value for any given kernel. Past that point, higher timeout values you request have no effect — the connection will still time out sooner.
TCP connects are special because establishing a connection involves a specific sequence of packets, starting with a SYN packet. If no response arrives for that initial SYN, the kernel retries — possibly a few times. Every kernel I know of waits an increasing amount of time between SYN retries, to avoid flooding slow hosts.
Every kernel also puts an upper limit on the number of SYN retries. On BSD-derived kernels, including macOS, the standard pattern is a second SYN 6 seconds after the first, a third SYN 18 seconds after that, and the connection times out after roughly 75 seconds total.
On Linux, however, the default retry cycle ends after just 20 seconds. Linux sends SYN retries somewhat faster than BSD-derived kernels — reportedly 5 SYNs within that 20 seconds, including the original packet (retries after 3s, 6s, 12s, 24s).
The end result: if your application wants a connect timeout shorter than 20s, no problem — but if it wants one longer than 20s, the default kernel configuration will effectively cap it at 20s.
Changing this upper limit is easy, though it requires a system configuration change and root access (or a system administrator willing to make the change).
The relevant sysctl is tcp_syn_retries — for IPv4, net.ipv4.tcp_syn_retries.
Be conservative with the value. Like BSD, the SYN retry delays increase over time (doubling rather than tripling), so a relatively small increase in retry count leads to a much larger increase in the maximum connect timeout. In an ideal world a very high timeout wouldn’t matter, since applications’ own connect timeouts would kick in first — but many applications don’t set an explicit connect timeout, so setting the kernel value to, say, 10 minutes risks something hanging for ages when a remote host goes down.
A value of 6, 7, or at most 8 is a reasonable choice: 6 gives an effective ceiling of around 45 seconds, 7 gives around 90 seconds, and 8 gives around 190 seconds.
To change it on a running kernel via /proc:
# cat /proc/sys/net/ipv4/tcp_syn_retries
5
# echo 6 > /proc/sys/net/ipv4/tcp_syn_retries
Or with sysctl:
# sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 5
# sysctl -w net.ipv4.tcp_syn_retries=6
net.ipv4.tcp_syn_retries = 6
To persist the change across reboots, add it to /etc/sysctl.conf:
net.ipv4.tcp_syn_retries = 6
Most Linux installs also support reading sysctls from files under /etc/sysctl.d, which
is usually better practice since it makes upgrades easier to administer — so consider
putting it there instead.
(There’s no real reason to reduce this sysctl, but note that values of 4 or less all seem to be treated as 4 — a total timeout of about 9 seconds.)