Network Debugging
A network failure rarely means “the network is down.” A machine can have a perfectly working network interface while having no usable route, a working route while DNS is broken, or working DNS while the application itself is not listening on the expected port.
The useful debugging skill is therefore not memorizing one magic command. It is narrowing the failure down layer by layer.
Debug From The Bottom Up
A practical Linux networking investigation can follow this path:
Network interface
↓
IP configuration
↓
Routing
↓
DNS
↓
Port / connection
↓
Application protocolEach step answers a different question:
| Layer | Question |
|---|---|
| Interface | Is the network interface present and up? |
| IP | Does the machine have the expected address? |
| Routing | Does the kernel know where to send the packet? |
| DNS | Can the hostname be resolved? |
| Port | Is something reachable/listening on the expected port? |
| Application | Does the service actually respond correctly? |
The key is to avoid jumping immediately to the application. If the machine cannot resolve a hostname or has no route, debugging HTTP headers will not help.
Start With The Interface
First inspect the interfaces:
ip linkLook for the expected interface and whether it is operational.
Then inspect its addresses:
ip addrYou are checking basic facts such as:
Is the interface present?
Is it UP?
Does it have an IP address?
Does the address look correct?For example, an interface might appear as:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet 192.168.1.20/24 ...This tells you that the interface exists, is up, and has an IPv4 address.
If the interface is missing or down, there is little point debugging DNS or HTTP yet.
Check The Routing Table
Once the interface and IP address look correct, ask the kernel where it would send traffic.
ip routeYou may see:
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.20The important entry for ordinary Internet access is usually:
default via 192.168.1.1 dev eth0That means traffic for destinations without a more specific route should be sent to:
192.168.1.1through:
eth0Ask The Kernel Which Route It Will Use
Instead of merely reading the routing table, you can ask Linux to perform a route lookup:
ip route get 8.8.8.8The result tells you which route, interface, source address, and next hop Linux would use for that destination.
This is particularly useful when a machine has multiple interfaces.
For example:
Network
│
├── eth0 → network A
│
└── eth1 → network BLooking at ip route tells you what routes exist.
ip route get answers the more practical question:
For this particular destination, what will Linux actually do?
Test Basic IP Connectivity
If the interface and routing look reasonable, test a known IP address:
ping 8.8.8.8If this succeeds, you have evidence that:
interface
↓
IP configuration
↓
routing
↓
remote IP connectivityare functioning at least well enough for ICMP traffic to reach the destination and return.
If it fails, don’t immediately conclude that the route is broken.
The destination might simply block ICMP.
That is why ping is a useful diagnostic signal, not a universal proof of connectivity.
What Does A Failed ping Actually Tell You?
Suppose:
ping 8.8.8.8fails.
Possible causes include:
Interface down
↓
Wrong/missing IP configuration
↓
Missing route
↓
Gateway problem
↓
Remote network problem
↓
ICMP blockedSo the correct response is not:
“Ping failed, therefore the Internet is down.”
Instead:
“This particular ICMP test did not succeed. Now determine which part of the path failed.”
That distinction is important in real troubleshooting.
Test DNS Separately
Now try a hostname:
ping example.comThis test combines two things:
DNS resolution
+
ICMP connectivityThat makes it less precise than testing an IP address directly.
Suppose:
ping 8.8.8.8works, but:
ping example.comfails with a name-resolution error.
That is a strong clue:
IP connectivity works
↓
DNS resolution is the likely problemNow inspect the resolver configuration:
cat /etc/resolv.confYou may see nameserver entries such as:
nameserver 192.168.1.1You can also inspect /etc/hosts:
cat /etc/hostsRemember that /etc/hosts can provide local hostname mappings without consulting DNS.
Use getent To Test Name Resolution
A useful Linux-native way to test name resolution is:
getent hosts example.comIf it returns an address:
93.184.216.34 example.comthe system was able to resolve the name through its configured name-service mechanism.
This can be more useful than testing with ping, because it isolates the name-resolution part.
For example:
getent hosts example.comfails:
DNS/name resolution problemwhile:
getent hosts example.comworks but:
ping example.comfails:
Name resolution is probably fine.
Investigate connectivity or ICMP behavior.Compare Hostname And IP Tests
This gives you a simple diagnostic pattern.
Start with:
ping 8.8.8.8Then:
getent hosts example.comThen:
ping example.comThe results help separate the problem:
IP test fails
↓
Look at interface, IP, route, gateway, connectivity
IP test works
+
Name resolution fails
↓
Look at DNS
IP test works
+
Name resolution works
+
Hostname ping fails
↓
Look at ICMP / destination behaviorDon’t treat these as absolute rules. They are clues that narrow the search.
Check Whether A Port Is Reachable
Suppose the server is reachable, but an application is unavailable on TCP port 8080.
You need a different test.
A simple TCP connection test is:
nc -vz example.com 8080The options mean:
-v
verbose output
-z
scan/check without sending application dataIf the connection succeeds, you have evidence that TCP connectivity to that port exists.
If it fails, the problem may be:
No service listening
↓
Firewall filtering
↓
Wrong address/port
↓
Routing/connectivity problemAgain, the result narrows the possibilities rather than identifying one cause automatically.
Check Local Listening Ports
If the service is supposed to run on your own machine, inspect listening sockets:
ss -lntYou might see:
LISTEN 0 128 0.0.0.0:8080 0.0.0.0:*This tells you that something is listening on TCP port 8080.
If you expect a service on port 8080 but ss shows nothing listening there, the problem is local to the service or its configuration. There is no reason to start debugging DNS yet.
For a more useful view that includes the owning process:
sudo ss -lntpThis can show which process owns the listening socket.
127.0.0.1 Versus 0.0.0.0
One common source of confusion is the address a service binds to.
Suppose a Python service listens on:
127.0.0.1:8080That means it is listening only on the local loopback interface.
A remote machine cannot normally connect to it through the machine’s external IP.
Compare:
127.0.0.1:8080with:
0.0.0.0:8080The first means:
local machine onlyThe second means:
listen on all IPv4 interfacesThis is an application-level configuration issue that often looks like a networking failure from the outside.
Test The Service Locally First
Suppose a web service should be available on port 8080.
Start by testing from the same machine:
curl http://127.0.0.1:8080If that works, the application is at least responding locally.
Now try the machine’s actual IP:
curl http://192.168.1.20:8080If this fails while the loopback request works, inspect how the service is bound.
For example:
sudo ss -lntpIf you see:
127.0.0.1:8080you have found a likely explanation.
The service is alive, but it is not listening on the network interface you are trying to reach.
Test The Application Layer
Once lower-level connectivity works, use the protocol-specific tool.
For HTTP:
curl -v http://example.comNow you can distinguish:
TCP connection succeeds
↓
HTTP request sent
↓
HTTP response receivedIf you receive:
HTTP/1.1 404 Not Foundthe network worked.
The server received your request and deliberately returned an HTTP response.
That is very different from:
Connection refusedor:
Connection timed outThose occur before a normal HTTP response is received.
Read The Error As A Layer Clue
Consider three different outcomes.
Connection Refused
curl: (7) Failed to connect ... Connection refusedThis generally means the destination was reachable enough to reject the TCP connection, commonly because nothing is listening on that port.
Check:
ss -lnton the destination.
Connection Timed Out
A timeout is different.
It can indicate that packets are being dropped somewhere along the path, a firewall is silently filtering the traffic, or the destination is otherwise unreachable.
Now investigate:
ip routeand:
ping <destination-ip>where appropriate.
You may also use:
nc -vz <destination> <port>to test the specific TCP port.
HTTP Error
Suppose:
curl https://example.com/apireturns:
HTTP/1.1 500 Internal Server ErrorThis means the network path and HTTP exchange worked well enough for the server to return a response.
The problem has moved upward into the application/service layer.
This is an important debugging boundary:
Connection refused
→ TCP/service problem
Connection timeout
→ connectivity/filtering/path problem
HTTP 500
→ application/server problemA Practical Debugging Flow
Suppose someone reports:
“The server cannot reach
https://example.com.”
Don’t immediately start changing configuration.
Walk through the path:
flowchart TD
A["Start: Network request fails"] --> B["Check interface and IP<br/>ip link / ip addr"]
B --> C["Check route<br/>ip route / ip route get"]
C --> D["Test known IP<br/>ping"]
D --> E["Test name resolution<br/>getent hosts"]
E --> F["Test TCP port<br/>nc -vz"]
F --> G["Test application<br/>curl -v"]
G --> H["Inspect application response"]
Each step removes a category of possible problems.
A More Concrete Example
Imagine this command fails:
curl https://example.comStart with:
ip linkThe interface exists and is up.
Then:
ip addrThe machine has a valid IP address.
Then:
ip routeA default route exists.
Test an IP:
ping 8.8.8.8This works.
Now test resolution:
getent hosts example.comSuppose this fails.
At this point, don’t investigate TCP port 443.
The evidence already tells you:
Interface ✓
IP ✓
Route ✓
IP connectivity ✓
DNS ✗The investigation has narrowed to name resolution.
That is the entire point of layered troubleshooting.
Another Example: DNS Works, HTTP Does Not
Suppose:
getent hosts example.comworks.
Then:
curl -v https://example.comfails to connect.
Now the problem is no longer basic DNS.
You can test TCP:
nc -vz example.com 443If that fails, investigate the TCP path and filtering.
If it succeeds, but curl fails during TLS negotiation, the problem has moved above basic TCP connectivity.
This is much more efficient than randomly changing DNS settings, routes, or interface configuration.
Debug A Local Service
Suppose you have a service expected at:
192.168.1.20:8080First:
sudo ss -lntpIf nothing is listening on 8080, fix the service.
If you see:
127.0.0.1:8080the service is local-only.
If you see:
0.0.0.0:8080it is listening on all IPv4 interfaces, so investigate the network path or filtering if remote clients still cannot connect.
From another machine:
nc -vz 192.168.1.20 8080Then:
curl http://192.168.1.20:8080This gives you a clean progression:
Is a process listening?
↓
Can TCP reach it?
↓
Does the application respond?Don’t Use ping As The Only Network Test
ping is useful because it tests IP-level reachability using ICMP.
But an application might be perfectly reachable even when ping fails.
For example:
ping example.com
→ ICMP blocked
curl https://example.com
→ HTTP worksThere is no contradiction.
Different protocols can be treated differently by firewalls and hosts.
For application debugging, test the application protocol itself.
For HTTP:
curl -v https://example.comFor a raw TCP port:
nc -vz example.com 443For IP-level reachability:
ping example.comEach command answers a different question.
A Useful Command Sequence
When you don’t know where a network problem is, this is a practical starting sequence:
ip link
ip addr
ip route
ip route get 8.8.8.8
ping 8.8.8.8
getent hosts example.com
nc -vz example.com 443
curl -v https://example.comYou won’t always need every command.
The sequence moves from:
local configuration
↓
routing
↓
IP connectivity
↓
DNS
↓
TCP
↓
applicationStop as soon as the evidence identifies the layer that is failing.
What You Should Remember
Network debugging becomes much easier when you stop treating “the network” as one thing.
A failure can occur at several distinct layers:
Interface
↓
IP address
↓
Routing
↓
DNS
↓
TCP port
↓
Application protocolUse the command that matches the question:
ip link
→ Is the interface up?
ip addr
→ Does it have the expected address?
ip route
→ Where will packets go?
ip route get
→ What route will Linux actually choose?
ping
→ Can IP/ICMP reach the destination?
getent hosts
→ Can the system resolve the name?
ss
→ What sockets are listening or connected?
nc
→ Can I establish a TCP connection to this port?
curl
→ Does the application protocol actually work?The goal isn’t to run every command every time.
The goal is to move from one layer to the next until you have enough evidence to explain the failure.
What’s Next
The next topic steps back from troubleshooting individual connections and looks at something deeper: how the Linux kernel participates in networking. You’ll see where interfaces, IP, routing, sockets, and packet processing fit inside the kernel, and why commands such as ip, ss, and curl are really different views into the same underlying networking system.