Network CLI Toolkit
Diagnose DNS, connections, and packets from the terminal — dig, ss, tcpdump, ip, and nc, with the flags that get answers fast.
Debug a network one layer at a time: name (does DNS return the right address?), route (is there a path?), port (is something listening and reachable?), then protocol (do TLS and HTTP behave?). The sections follow that order, with packet capture last — start at the top and stop at the first layer that looks wrong.
Commands target Linux (ss and ip from iproute2), with macOS equivalents noted. Add -n wherever it exists: skipping reverse-DNS lookups makes output faster and stops a broken resolver from hanging the very tool you are debugging it with.
DNS (dig)
- Resolve a name
dig example.com +short- Specific record
dig example.com MX +short(A/AAAA/MX/TXT/NS/CNAME)- Use a resolver
dig @1.1.1.1 example.com- Trace delegation
dig +trace example.com— root → TLD → authoritative- Reverse lookup
dig -x 8.8.8.8 +short- dig +noall +answer example.com
- only the answer lines, with the remaining TTL — how long caches keep it
- dig example.com SOA +short
- primary nameserver and zone serial; the last number is the negative-caching TTL
- dig +tcp example.com
- query over TCP — exposes firewalls that drop DNS over TCP or large UDP answers
- host example.com
- quick readable lookup: addresses and mail servers in one go
- getent hosts example.com
- resolve the way applications do (honors
/etc/hosts); dig skips that file - resolvectl status
- which DNS servers systemd-resolved uses per interface; macOS:
scutil --dns
Interfaces & routes (ip)
- Addresses
ip -br a(brief)- Routing table
ip route· default gateway shown first- Which route to a host
ip route get 8.8.8.8- Path to host
mtr example.com(live traceroute + loss)- ip -br link
- every interface with its state (UP/DOWN) and MAC address
- ip neigh
- the ARP / neighbor table: IP to MAC on the local network
- ip -s link show eth0
- packet, error and drop counters for one interface
- ip addr add 10.0.0.2/24 dev eth0
- add an address until reboot (needs root);
ip addr delremoves it - netstat -rn
- routing table on macOS/BSD;
route -n get 8.8.8.8shows the route to one host - ifconfig
- the macOS/BSD interface view; deprecated on Linux in favor of
ip
Reachability
- ping -c 4 host
- round-trip time and loss; many hosts drop ICMP, so no reply does not mean down
- ping -M do -s 1472 host
- path-MTU test (Linux): 1472 + 28 header bytes = 1500, no fragmentation allowed
- traceroute -n host
- every hop to the target;
* * *often just means a router filters probes - traceroute -T -p 443 host
- trace with TCP SYNs to a real port, past filters that drop UDP and ICMP (Linux, root)
- mtr -rwc 100 host
- report mode, 100 probes per hop — only loss that persists to the last hop is real
- nc -vz -w 3 host 443
- TCP connect test with a timeout; on macOS the connect timeout flag is
-G 3 - nc -l 8080
- listen on a port to test a firewall from the far side; traditional netcat wants
-l -p 8080 - nc -vzu host 53
- UDP check — unreliable, because UDP never confirms that a port is open
Connections & ports (ss)
- Listening TCP + PID
ss -ltnp- All established
ss -tn state established- By port
ss -tnp 'sport = :443'- Socket summary
ss -s— counts by state- Is a host reachable?
nc -vz host 443— TCP connect test- ss -ulnp
- listening UDP sockets with the owning process
- ss -ti dst 10.0.0.5
- TCP internals per connection — rtt, cwnd, retransmits: the “why is it slow” view
- lsof -i :8080
- which process holds a port — works on macOS as well as Linux
- lsof -iTCP -sTCP:LISTEN -nP
- every listening TCP port with its process, without name lookups
- netstat -tulpn
- the legacy equivalent of
ss -tulpn(net-tools, often not installed)
HTTP & TLS
- curl -v https://host
- DNS, connect, TLS handshake, then request and response headers, step by step
- curl -s -o /dev/null -w '%{http_code}'
- just the status code — for scripts and health checks
- -w %{time_namelookup} … %{time_total}
- curl timings: DNS, connect, TLS (
time_appconnect), first byte, total - curl --resolve host:443:10.0.0.5 url
- pin a name to an IP with SNI and Host intact — test a backend before DNS cutover
- curl -I url
- HEAD request: status and headers only (some servers answer HEAD differently from GET)
- openssl s_client -connect host:443
- raw TLS handshake: chain, protocol, cipher; add
-servername hostfor SNI - openssl x509 -noout -dates
- pipe
s_clientinto it for the live certificate’s notBefore / notAfter - openssl x509 -checkend 2592000
- exits 1 if the certificate expires within 30 days — a one-line expiry alert
Packets (tcpdump)
- Capture on interface
tcpdump -i eth0 -nn(-nn= no name/port resolve)- Filter host + port
tcpdump -i any host 10.0.0.5 and port 80- Write / read pcap
tcpdump -w cap.pcap· open in Wireshark- See payloads
tcpdump -A -nn port 80(ASCII)- tcpdump -D
- list capture interfaces;
-i anycaptures on all of them (Linux) - tcpdump -c 100
- stop after 100 packets — never leave a capture unbounded on a busy host
- tcpdump -r cap.pcap port 443
- read a saved capture and filter it after the fact
- tcpdump -nn 'not port 22'
- leave out your own SSH session, or the capture fills with itself
- 'tcp[tcpflags] == tcp-syn'
- pure SYNs only: every new connection attempt and its destination
- 'tcp[tcpflags] & (tcp-rst) != 0'
- resets only: who is refusing or killing connections
- tcpdump -X
- hex plus ASCII payload;
-Ais ASCII only
Recipes
Is it DNS? Check every resolver layer
dig +short api.example.com # your configured resolver
dig +short api.example.com @1.1.1.1 # a public resolver
dig +short api.example.com @ns1.example.com # the authoritative server itself
getent hosts api.example.com # what applications see, /etc/hosts included
dig +noall +answer api.example.com # the TTL: how long a stale answer can liveIf the authoritative server is right and your resolver is wrong, you are waiting out a cached TTL. If getent disagrees with dig, look at /etc/hosts or a VPN’s resolver settings.
Find what is listening on a port, then stop it
ss -ltnp 'sport = :8080' # Linux: PID in the users:(...) column
lsof -nP -iTCP:8080 -sTCP:LISTEN # macOS or Linux
kill 12345 # the PID either command printed; -9 only if it ignores thisss -p and lsof need root to show processes owned by other users — without it the process column is just empty, not an error.
Find the slow phase of an HTTP request
curl -s -o /dev/null \
-w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
https://api.example.com/healthEach value is cumulative seconds from the start, so subtract neighbors: a big gap from connect to tls is the handshake, from tls to ttfb is the server thinking.
Check a certificate’s expiry and issuer
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# exits 1 if it expires within 30 days (2592000 seconds)
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -checkend 2592000 || echo "renew soon"Without -servername, a server hosting several sites may return its default certificate rather than the one browsers see. The echo | closes the connection once the handshake has printed.
Capture a problem for Wireshark
sudo tcpdump -i any -nn -c 2000 -w /tmp/api.pcap 'host 10.0.0.5 and port 443'
# reproduce the problem, then take a quick look:
tcpdump -nn -r /tmp/api.pcap | head -20
# and open /tmp/api.pcap in Wireshark for the full decodeHTTPS payloads stay encrypted in the capture, but handshakes, resets, retransmissions and timing are all visible — which is usually where the bug is.
Questions about Network CLI Toolkit Cheat Sheet
What is the difference between ss and netstat?
Both list sockets, but ss is the modern replacement from the iproute2 package and reads socket state directly from the kernel over netlink, so it stays fast on servers with tens of thousands of connections. netstat comes from the older net-tools package, parses files under /proc, and is no longer installed by default on many distributions. The common flags carry over: ss -tulpn shows listening TCP and UDP sockets with their processes, just as netstat -tulpn did. ss also adds filters by state and port, and per-connection TCP details with -i. On macOS, which has no ss, use netstat or lsof.
How do I check if a port is open on a remote host?
Run nc -vz host port, which attempts a TCP connection and reports success or refusal without sending data; add a timeout such as -w 3 on Linux, or -G 3 on macOS, so a silently filtered port does not hang. A refused connection means the host answered but nothing is listening, while a timeout usually means a firewall dropped the packets. curl -v telnet://host:port works where nc is missing. UDP is harder to check, because a UDP port can stay silent whether open or not. Only test hosts you are responsible for.
Why does dig show a different answer than my browser or application?
dig talks straight to a DNS server and ignores the rest of the system’s name resolution. Applications go through the system resolver, which also consults /etc/hosts, local caches such as systemd-resolved or the macOS resolver, VPN split-DNS rules, and sometimes DNS over HTTPS built into the browser. Each layer can hold its own cached answer until its TTL expires. To see what applications see, use getent hosts on Linux or dscacheutil -q host -a name on macOS, and compare dig answers from several resolvers to find the layer that is out of date.
How do I find which process is using a port?
On Linux, run ss -ltnp and look for the port in the local address column: the users field shows the process name and PID. To filter directly, use ss -ltnp with a sport = :8080 filter in quotes. On macOS or Linux, lsof -nP -iTCP:8080 -sTCP:LISTEN prints the command, PID and user. Both need root to show processes owned by other users, so run them with sudo if the output looks empty. Once you have the PID, stop the process gracefully with kill, and reach for kill -9 only if it refuses to exit.