linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
Index
Categories
All entries
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
home/linux/kb/Networking: L4 and above/tcp-keepalive

kb/network-l4 ── Networking: L4 and above ── intermediate

TCP keepalive

Keepalive sends probes on an idle TCP connection to detect a dead peer (NAT timeout, crashed host). Linux defaults: 7200s idle, 75s between probes, 9 probes. Enabled via setsockopt(SO_KEEPALIVE).

view as markdownaka: keepalive, tcp-keep-alive, so-keepalive

Why keepalive

TCP without traffic has no way to know whether the peer is alive. If a client silently unplugs or NAT closes the mapping, the server finds out only when it tries to send data. On long-lived connections (DB pools, WebSockets, queues) this is a problem: a thread sits blocked on a zombie connection.

Keepalive is a kernel mechanism: after N seconds of silence the kernel sends a probe ACK with a stale sequence number. If the peer is alive it returns an ACK; if dead, nothing comes back, and after M probes the kernel closes the socket with ETIMEDOUT.

Three host-level tuning knobs

sysctldefaultmeaning
net.ipv4.tcp_keepalive_time7200 (2 h)seconds of silence before the first probe
net.ipv4.tcp_keepalive_intvl75interval between probes
net.ipv4.tcp_keepalive_probes9number of probes before declaring the peer dead

Default = 2 hours idle + 9x75s = roughly 2 hours 11 minutes before close. For most workloads that is far too long.

Enabling keepalive in an application

The socket option SO_KEEPALIVE is off by default. You must set it explicitly:

python
import socket
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# Per-socket override (Linux):
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)   # 60s idle before first probe
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10)  # 10s between probes
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)     # 3 probes

Result: 60 + 3x10 = 90 seconds from silence to close.

When to tune

  • DB pools (PostgreSQL/MySQL): the default 2-hour window means that after a DB restart the pool holds dead sockets until the first SQL query. Set keepalive to 30-60 seconds.
  • WebSocket / gRPC with a proxy in between: NAT/LB devices typically close idle connections after 5-10 minutes. Keepalive every 30-60 seconds prevents that.
  • VPN / SSH tunnels: same issue.
  • API behind cloud LB: AWS NLB closes idle connections after 350s by default.

Keepalive vs application ping

ApproachProsCons
TCP keepalivefree, handled by the kerneldoes not verify that the application is alive, only the socket; does not work through L7-proxying proxies
Application pingchecks the full chain to the handlerrequires implementation, adds traffic

For [[websocket|WebSocket]] the right answer is both: keepalive catches a broken TCP connection, and an application-level ping (frame opcode 0x9) catches a hung server.

What you see in tcpdump

A keepalive probe is a packet with seq = current_seq - 1, no payload, ACK flag. In tcpdump look for an empty ACK arriving at the tcp_keepalive_intvl interval after the previous traffic.

IP 10.0.0.1.443 > 10.0.0.5.34521: Flags [.], ack 100, win 1024, length 0

Notes

  • Keepalive keeps the NAT mapping alive precisely because it sends packets. Set tcp_keepalive_time lower than the NAT timeout and NAT will not close the mapping.
  • On modern Linux there is TCP_USER_TIMEOUT as an alternative: close the connection if no ACK is received for sent data within N milliseconds. It is often more useful than keepalive because it works under load too.

Troubleshooting

  • Connection hangs after 5 minutes idle without closing: keepalive is off, or tcp_keepalive_time exceeds the NAT timeout.
  • Probes are too frequent and noisy: tcp_keepalive_intvl is too small, or TCP_KEEPIDLE is set to 5 seconds (excessive).
  • Connection closed after 5 minutes despite active traffic: not keepalive; check the NAT/LB config (idle timeout is separate from keepalive).
  • error: ETIMEDOUT on send(): keepalive fired and the peer is dead.

§ команды

bash
sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes

Show current global keepalive defaults

bash
sudo sysctl -w net.ipv4.tcp_keepalive_time=60

Reduce idle threshold to 60s globally (requires sysctl persist or reboot to survive)

bash
ss -tnoH state established | head

The timer column shows keepalive status: 'keepalive(45sec,...)' when enabled

bash
ss -tnoeH | awk '/keepalive/ {print}'

Show only sockets with an active keepalive timer

§ см. также

  • tcp-handshakeTCP three-way handshakeTCP connection opens with three packets: SYN from the client, SYN-ACK from the server, ACK from the client. After that the connection is Established and data transfer can begin.
  • tcp-statesTCP states (LISTEN, ESTABLISHED, TIME_WAIT)A TCP session moves through 11 states from LISTEN to CLOSED. The most important in production: LISTEN, ESTABLISHED, TIME_WAIT, CLOSE_WAIT.
  • portPort: How Multiple Services Share One IPA 16-bit number (0-65535) that identifies the **destination process** on a host. IP says which host; port says which process. 80 is HTTP, 443 is HTTPS, 22 is SSH.
  • cmd-iperf3iperf3: measuring bandwidth`iperf3` measures TCP/UDP throughput between two endpoints. Run a server on one host and a client on the other. Use it for network testing, not in production.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies