linux-network-debugging
Skill0 starsChanged 2 months agoApache-2.0View on GitHub
Debug network failures from a Linux host - connection refused versus timeout, conntrack table exhaustion, listen backlog overflow, ephemeral port exhaustion, TIME_WAIT accumulation, MTU and path MTU black holes, packet loss, and routing or firewall drops. Use when connections fail, hang, or reset from a host, and when tcpdump or ss output needs interpreting.
In this repository26 other files it gives its agents.
- AGENTS.mdAGENTS.md
- aws-production-debuggingSkill
- github-actions-debuggingSkill
- postgresql-troubleshootingSkill
- container-image-reviewSkill
- docker-debuggingSkill
- incident-triageSkill
- postmortem-analysisSkill
- production-debuggingSkill
- kubernetes-networkingSkill
- kubernetes-production-readinessSkill
- kubernetes-resource-debuggingSkill
- kubernetes-troubleshootingSkill
- linux-performanceSkill
- linux-troubleshootingSkill
- dns-troubleshootingSkill
- http-debuggingSkill
- tls-debuggingSkill
- observability-debuggingSkill
- prometheus-troubleshootingSkill
- slo-designSkill
- secrets-management-reviewSkill
- technology-jobSkill
- terraform-reviewSkill
- terraform-safe-changesSkill
- terraform-troubleshootingSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 55
Capture packets: only when the layers above have not answered it.
- Very specific: line 175
Always use -n to skip reverse DNS, which otherwise adds latency and generates its own traffic.
- Very specific: line 176
Always bound the capture with -c or a filter.
- Distinctive: line 289
Better: Only loss that persists to the final hop is real.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Linux Network Debugging
Work down the stack — socket, host, path — and let the failure mode tell you which layer to look at.
Purpose
Use this when:
- Connections from a host fail with
connection refused,connection timed out, orconnection reset by peer. - Throughput is poor or transfers stall at a consistent size.
nf_conntrack: table fullappears indmesg.- You need to interpret
ss,netstat,tcpdumporconntrackoutput.
Do not use this when:
- The failure is name resolution — use
dns-troubleshooting.no such hostis never a TCP problem. - The failure is HTTP-level (status codes, keepalive, proxies) — use
http-debugging. - The failure is inside Kubernetes Services or NetworkPolicies — use
kubernetes-networking.
What this skill assumes: shell access on at least one end of the connection, and sudo for
packet capture and conntrack. Capturing on both ends is worth the effort: it is the only way to
tell "we never sent it" from "they never received it".
Operating Procedure
- Read the error precisely. Refused, timeout and reset are three different layers.
- Confirm the target. Resolve the name, and note the IP you are actually connecting to. Half of "network" problems are the client talking to the wrong address.
- Test the socket layer with
sslocally: is anything listening, is the backlog overflowing. - Test the path with
curl,nc, ormtr. One hop at a time. - Check host-level limits: conntrack, ephemeral ports, backlog, fd limits.
- Capture packets only when the layers above have not answered it.
tcpdumpis precise and slow to read; it is a third step, not a first one. - Fix and verify with the same test that failed.
tcpdump output is the strongest Observed evidence available here. Everything inferred from
counters is weaker; say which you are using.
Investigation
Step 0 - what does the failure mean
| Symptom | Layer | Meaning |
connection refused | TCP | A RST came back. The host is reachable and nothing is listening on that port. |
connection timed out | IP/filter | No response at all. A firewall dropped it, the route is wrong, or the host is down. |
connection reset by peer | TCP | An established connection was killed — the peer, a middlebox, or a conntrack entry expiring. |
no route to host | IP | Local routing table has no path, or an ICMP unreachable came back. |
| Connects, then hangs on large transfers | Path MTU | Small packets pass, large ones are dropped and ICMP is blocked. |
| Works, then fails after N connections | Host limits | Ephemeral ports, conntrack, or fd exhaustion. |
Refused versus timeout is the single most valuable distinction: refused proves packets reach the host and come back, which eliminates the entire firewall and routing layer in one observation.
Step 1 - the socket layer
ss -lntp # listening TCP sockets with the owning process
ss -s # summary by state
ss -tn state established '( dport = :5432 )' | wc -l
ss -lnt '( sport = :8080 )' # Send-Q on a listening socket = accept backlog sizeOn a listening socket, Recv-Q is the number of connections waiting to be accepted and
Send-Q is the configured backlog. Recv-Q approaching Send-Q means the application is not
calling accept() fast enough, and new connections are being dropped — which the client sees as a
timeout, not as a refusal.
nstat -az TcpExtListenOverflows TcpExtListenDrops TcpExtTCPSynRetransListenOverflows incrementing is unambiguous proof of backlog overflow. This is a common
production failure with a confusing signature: the server is healthy, the port is open, and clients
time out.
Step 2 - host-level limits
# conntrack
sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
sudo dmesg -T | grep -i conntrack
# ephemeral ports
sysctl net.ipv4.ip_local_port_range
ss -tan state time-wait | wc -l
# per-process sockets
sudo ls /proc/<pid>/fd | wc -lConntrack tracks every connection through a NAT or stateful firewall. When
nf_conntrack_count approaches nf_conntrack_max, new connections are dropped silently and
dmesg logs table full, dropping packet. This presents as random timeouts under load and is
frequently misattributed to the application. It is especially common on Kubernetes nodes and NAT
gateways.
Ephemeral port exhaustion has a similar signature. A host making many short-lived outbound
connections to the same destination can exhaust the ~28k default range; TIME_WAIT sockets hold
ports for 60 seconds after close. A large time-wait count is normal and usually harmless — it
only matters when the port range is genuinely exhausted, so count before concluding.
Step 3 - the path
curl -sS -o /dev/null -m 10 -w \
'dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s code=%{http_code}\n' \
https://<host>/
nc -vz <host> <port>
mtr -rwzc 50 <host>
ip route get <destination-ip>The curl timing breakdown localises the delay immediately: a slow time_namelookup is DNS, a
slow time_connect is the TCP handshake (network path), a slow time_appconnect is TLS, and a
slow time_starttransfer with fast connect is the server thinking.
mtr sends 50 probes and shows per-hop loss. Read it carefully: loss at an intermediate hop that
does not persist to the final hop is not real. Routers deprioritise ICMP to their own control
plane. Only loss that continues to the destination row counts.
ip route get shows which interface and gateway the kernel will actually use, including
policy-based routing — the fastest way to catch a host that has two interfaces and is sending
traffic out the wrong one.
Step 4 - MTU and path MTU discovery
ip link show # MTU per interface
ping -M do -s 1472 <destination> # 1472 + 28 = 1500
ping -M do -s 1400 <destination>-M do sets Don't Fragment. If 1400 succeeds and 1472 fails with Message too long locally,
your own MTU is smaller. If 1472 simply times out while small packets work, you have a path MTU
black hole: something on the path is dropping large packets and the ICMP "fragmentation needed"
message is being filtered.
The signature is distinctive and very commonly misdiagnosed: TCP connections establish fine, small requests work, and anything large hangs forever. It appears constantly with VPNs, overlay networks and tunnels, where encapsulation reduces the usable MTU below 1500.
Step 5 - packet capture
sudo tcpdump -ni any -c 200 "host <ip> and port <port>"
sudo tcpdump -ni any -w /tmp/cap.pcap "host <ip> and port <port>"
sudo tcpdump -nr /tmp/cap.pcap | head -50Always use -n to skip reverse DNS, which otherwise adds latency and generates its own traffic.
Always bound the capture with -c or a filter; an unfiltered capture on a busy host fills the disk
and adds load.
What to look for:
| Pattern | Meaning |
SYN with no SYN-ACK, repeated | Dropped inbound. Firewall, security group, or wrong destination. |
SYN then RST immediately | Nothing listening on that port. |
RST mid-conversation | Peer or middlebox killed an established connection. |
| Many retransmissions | Loss on the path. |
[F.] followed by silence, then RST | One side closed while the other kept writing. |
| Correct handshake, then nothing | Application accepted but never responded — server-side, not network. |
Capture on both ends where possible. Packets leaving one host and not arriving at the other locates the drop to the path; packets arriving and not being answered locates it to the far host.
Decision Tree
Connection problem
├── "connection refused" → packets arrive; nothing listening
│ ├── ss -lntp shows no listener → the service is down or bound elsewhere
│ └── Bound to 127.0.0.1 → binding, not networking
├── "connection timed out" → no answer
│ ├── tcpdump shows SYN out, nothing back → dropped on the path (firewall/SG/route)
│ ├── tcpdump shows nothing out at all → local routing or a local filter rule
│ ├── conntrack table full in dmesg → conntrack exhaustion
│ └── ListenOverflows rising → backlog overflow; the app is not accepting
├── "connection reset" mid-stream
│ ├── Regular interval (~minutes) → idle timeout on a middlebox or conntrack expiry
│ └── Under load → server closing; check its limits and logs
├── Small requests fine, large hang → path MTU black hole
├── Fails only past N connections → ephemeral ports, conntrack, or fd limits
└── Slow but working → mtr for loss; curl -w to localise the phaseCommon Causes
| Cause | Signal | Fix | Risk |
| conntrack table full | dmesg conntrack lines, random timeouts | Raise nf_conntrack_max; reduce connection churn | mutating |
| Listen backlog overflow | ListenOverflows rising | Raise somaxconn and the app's backlog | mutating |
| Bound to localhost | ss -lntp shows 127.0.0.1 | Bind 0.0.0.0 | mutating |
| Path MTU black hole | Large transfers hang | Lower MTU or clamp MSS on the tunnel | disruptive |
| Ephemeral port exhaustion | Port range full, many TIME_WAIT | Connection pooling; widen the range | mutating |
| Asymmetric routing | ip route get picks an unexpected interface | Fix routes or policy routing | disruptive |
| Firewall drop | SYN out, nothing back | Add the rule; check cloud security groups too | mutating |
| Idle timeout on a load balancer | Resets at a regular interval | TCP keepalive shorter than the LB idle timeout | mutating |
Safety
Read-only
ss, ip route get, ip link show, nstat, sysctl reads, conntrack -C, curl, nc -z,
ping, and mtr. These generate a little traffic but change nothing.
tcpdump is read-only with two real costs: it puts the interface into promiscuous mode, and an
unbounded capture can fill the disk on a host that may already be struggling. Always filter, always
bound with -c or a size limit, and write captures outside the filesystem you are protecting.
Capture files contain payloads. On an unencrypted protocol that includes credentials and personal
data. Treat a .pcap as sensitive: restrict it, and delete it when the investigation ends.
Potentially disruptive
sysctl -w net.netfilter.nf_conntrack_max=...— takes effect immediately host-wide. Raising it consumes kernel memory (roughly 300 bytes per entry); a large increase on a small node is a memory problem in a new place. Record the old value first.sysctl -w net.core.somaxconn=...— only helps if the application also raises its ownlisten()backlog. Changing one without the other achieves nothing.ip link set <dev> mtu <n>— takes effect immediately and can break connectivity to the host you are changing it on. On a remote host this can lock you out.iptables/nftrule changes — a mistaken rule can cut off your own session. Where the tooling supports it, use a scheduled rollback; otherwise have out-of-band console access first.conntrack -Dto clear entries — kills the affected established connections immediately.ss -Kto kill sockets — terminates live connections.
Destructive
This skill proposes no destructive operations. Flushing all firewall rules (iptables -F) to "test
whether the firewall is the problem" is not on this list because it should not be done: on a cloud
host it can remove the rules that protect the instance and leave it exposed, and on a Kubernetes
node it can break the CNI's own rules and take every pod on the node offline. Add a narrow,
temporary allow rule instead, and remove it afterwards.
Verification
Before: 8% of outbound connections time out; nf_conntrack_count 65530/65536
After: 0 timeouts over 30 min; nf_conntrack_count 41200/262144; no dmesg conntrack linesfor i in $(seq 1 100); do
curl -sS -o /dev/null -m 5 -w '%{http_code} %{time_connect}\n' https://<host>/ 2>&1
done | awk '{print $1}' | sort | uniq -c
nstat -az TcpExtListenOverflows TcpExtListenDrops
sudo sysctl net.netfilter.nf_conntrack_countLoop the test at least 100 times. Intermittent network faults — the only kind that reach this skill, since constant ones are found in minutes — will pass a single check by chance. Verify under the load that produced the failure, and check that the counter which was climbing is now flat.
Anti-Patterns
Bad: ping works, so the network is fine.
Better: ping is ICMP. It proves nothing about a TCP port, a firewall rule scoped to that
port, MTU for large packets, or an application that is not accepting connections.
Bad: mtr shows 40% loss at hop 4, that is the problem.
Better: Only loss that persists to the final hop is real. Intermediate routers rate-limit ICMP
to their own control plane as a matter of policy.
Bad: Lots of TIME_WAIT sockets, so enable tcp_tw_recycle.
Better: That option was removed from the kernel in 4.12 because it broke connections from
clients behind NAT. TIME_WAIT is normal; only act if ports are genuinely exhausted, and then use
connection pooling.
Bad: Raise somaxconn to fix backlog overflow.
Better: The application's listen() backlog is capped by somaxconn but set independently.
Raising only the kernel limit changes nothing if the application asks for 128.
Bad: tcpdump first.
Better: ss and curl -w answer most questions in seconds. Packet capture is precise, slow to
read, and generates load on a host you may already be worried about.
Bad: Blame the network because the application logs a timeout. Better: A timeout is where the symptom appeared, not where the cause is. Check whether the server accepted the connection at all — the answer is frequently that it did, and then took 30 seconds to respond.
Composition
linux-troubleshooting— host resource exhaustion showing up as network failure.dns-troubleshooting— resolution failures, which are not TCP problems.kubernetes-networking— the same stack inside a cluster, with Services and policies added.http-debugging— once packets flow and the problem is at the protocol layer.
References
- iproute2
ssmanual — socket states and filters. - Linux kernel: TCP sysctl reference — every tunable mentioned here, with its real default.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

