“The network is broken” is one of the least useful sentences in system administration, because six different things can produce it and they need six different fixes. Getting from that sentence to an actual cause takes about a minute if you know what the layers are and which order to test them in.
This is the conceptual half. Networking basics covers the commands; this page covers what they are telling you.
The four things that must all work
| Layer | Answers | Broken means |
|---|---|---|
| Address | Who am I on this network? | No connectivity at all |
| Route | Where do I send packets for elsewhere? | LAN works, internet does not |
| DNS | What address is that name? | Addresses work, names do not |
| Port | Which program on that machine? | Host reachable, service is not |
Nearly every network problem is one of those four, and the symptom column tells you which. That table alone does most of the work.
Addresses and subnets
An IP address identifies an interface on a network. It always comes with a prefix — the /24 in 192.168.1.10/24 — and the prefix is what splits the address into “which network” and “which machine on it”.
The number is how many bits belong to the network. /24 means the first three of the four numbers identify the network, so 192.168.1.x is the network and x is the machine.
| Prefix | Netmask | Usable addresses |
|---|---|---|
/24 | 255.255.255.0 | 254 |
/25 | 255.255.255.128 | 126 |
/16 | 255.255.0.0 | 65,534 |
/32 | 255.255.255.255 | 1 — exactly this address |
Why this matters practically: two machines can talk directly only if they are on the same network by this calculation. If they are not, every packet goes to the gateway instead — and a mismatched prefix is a classic cause of “I can reach some machines on this LAN and not others”.
Certain ranges are reserved for private use and never routed on the internet:
| Range | Where you see it |
|---|---|
10.0.0.0/8 | Cloud VPCs, larger networks |
172.16.0.0/12 | Docker’s default bridge |
192.168.0.0/16 | Home routers |
127.0.0.0/8 | Loopback — this machine only |
169.254.0.0/16 | DHCP failed — self-assigned |
A 169.254.x.x address is worth recognising instantly: it means the machine asked for an address and nothing answered. The problem is DHCP or the physical link, not anything further up.
# What am I?
ip addr show
ip -brief addr # much easier to readRoutes and the gateway
The routing table answers one question for every outgoing packet: which interface, and to whom next?
ip route
# default via 192.168.1.1 dev eth0
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10Read that as two rules. The second says “anything in 192.168.1.0/24 is on my own network, send it straight out”. The first — the default route — says “everything else, hand to 192.168.1.1 and let it deal with it”.
Most specific match wins, and no default route means no internet while the local network keeps working perfectly. That combination is diagnostic on its own.
# Which route would this packet take?
ip route get 8.8.8.8
# Where does it break down?
traceroute 8.8.8.8
mtr 8.8.8.8 # traceroute and ping combined, liveip route get is underused and answers a real question: on a machine with several interfaces — a VPN, Docker bridges, two NICs — it tells you which one traffic to a given destination will actually use.
NAT, and why your address is not your address
Private addresses cannot be routed on the internet, so your router rewrites them. Outgoing packets get the router’s public address; replies get translated back. That is NAT, and it is why ip addr shows 192.168.1.10 while a website sees something entirely different.
# What the internet sees
curl -s https://api.ipify.org; echoThe consequence that shapes a lot of infrastructure: NAT is one-way by default. You can reach out; nothing can reach in, because the router has no idea which internal machine an unsolicited packet was meant for. Port forwarding exists to tell it, and Tailscale exists largely to avoid needing to.
Docker does the same thing on a smaller scale — containers get private addresses on a bridge network and are NATed out through the host, which is exactly why publishing a port is a separate deliberate act, and why those published ports bypass ufw. Linux firewalls covers that trap.
Ports
An address gets you to a machine. A port gets you to a program on it. A service listens on a port; a client connects from a random high-numbered one.
| Port | Service |
|---|---|
| 22 | SSH |
| 53 | DNS |
| 80 / 443 | HTTP / HTTPS |
| 3306 / 5432 | MySQL / PostgreSQL |
| 6379 | Redis |
The detail that matters most is which address a service binds to:
sudo ss -tulpn
| Local Address | Reachable from |
|---|---|
127.0.0.1:5432 | This machine only |
0.0.0.0:5432 | Every interface — only the firewall protects it |
192.168.1.10:5432 | That one interface |
Binding to localhost is a stronger control than a firewall rule, because it cannot be undone by a misconfigured rule elsewhere. Where a service supports it, bind it there and let the firewall be the backup.
One refinement to that, because there is a stronger control still. A service bound to 127.0.0.1 is still reachable by every user and every process on the machine — localhost is a network, and a port has no owner and no permissions. A Unix socket has no address at all: it lives in the filesystem with an owner, a mode and a directory you control, and the kernel will tell the server which process is on the other end. That is what your database, your container runtime, systemd and your logging daemon are all using locally. The Life of a Unix Socket follows one through all seven stages — starting with the fact that only the write bit gates a connection, so the mode that looks safest is usually the one that locks everybody out.
Two error messages worth distinguishing, because they point at different layers:
- Connection refused — you reached the machine and nothing was listening. The network is fine; the service is down or on another port.
- Connection timed out — nothing answered at all. A firewall dropped it, or you cannot reach the host.
DNS
DNS turns names into addresses, and it is responsible for a wildly disproportionate share of problems that feel like something else — because when it is slow, everything is slow, and when it is wrong, everything is wrong in confusing ways.
Resolution order on a Linux machine:
/etc/hosts— checked first, overrides everything. This is what makes the pre-DNS testing trick in migrating a server work.- The configured resolver, from
/etc/resolv.confor systemd-resolved. - The wider DNS hierarchy, if the resolver has no cached answer.
# Ask your configured resolver
dig example.com
dig +short example.com
# Bypass it and ask a public one - this is the key test
dig +short example.com @1.1.1.1
# Which resolver am I even using?
resolvectl status
cat /etc/resolv.conf
# Mail, name servers, everything
dig example.com MX
dig example.com NS
dig +trace example.comThe comparison is the diagnosis. If dig @1.1.1.1 returns the right answer and your normal resolver does not, the problem is your resolver or a stale cache — not the domain, and not the far end.
Use dig rather than ping to test DNS. ping mixes name resolution and reachability into a single result, so a failure tells you nothing about which one broke.
The diagnostic order
Work outwards. Each step assumes the previous one passed, so the first failure names the layer.
# 1. Do I have an address? (169.254.x.x means DHCP failed)
ip -brief addr
# 2. Is the interface actually up?
ip link
# 3. Can I reach my own gateway?
ip route
ping -c3 $(ip route | awk '/default/ {print $3}')
# 4. Can I reach the internet by address, ignoring DNS?
ping -c3 1.1.1.1
# 5. Does DNS work?
dig +short example.com
dig +short example.com @1.1.1.1
# 6. Is the service listening, and on what?
sudo ss -tulpn | grep :443
# 7. Can I reach that port from outside?
nc -zv example.com 443
curl -I https://example.com| First step to fail | Look at |
|---|---|
| 1 or 2 | Cable, driver, DHCP, virtual NIC |
| 3 | Local network, subnet mask, switch |
| 4 | Default route, upstream, provider |
| 5 | Resolver config, /etc/hosts, DNS records |
| 6 | The service — it is not running, or bound to localhost |
| 7 | Firewall, host or cloud security group |
Step 4 versus step 5 is the split worth internalising: if ping 1.1.1.1 works and dig example.com does not, your network is fine and DNS is broken. That single distinction resolves a large fraction of “the internet is down” reports.
Things that catch people out
| Symptom | Usually |
|---|---|
| Address is 169.254.x.x | DHCP got no reply |
| Some LAN hosts reachable, others not | Wrong subnet mask |
| Ping works, browsing does not | DNS |
| Works by IP, not by name | DNS, or a stale /etc/hosts entry |
| Connection refused | Nothing listening — check ss -tulpn |
| Connection timed out | Firewall dropping it silently |
| Works locally, not remotely | Service bound to 127.0.0.1 |
| DNS change not taking effect | TTL — the old answer is still cached |
| Container cannot resolve names | Docker DNS, not the host’s |
The TTL one catches people during migrations especially. A DNS record you changed is still cached by resolvers everywhere for as long as its TTL said, and nothing you do locally speeds that up — which is why lowering the TTL days in advance is the first step of any cutover.
Quick reference
| You want | Command |
|---|---|
| My addresses, readably | ip -brief addr |
| My gateway | ip route |
| Which route will be used | ip route get 8.8.8.8 |
| What is listening | sudo ss -tulpn |
| My public address | curl -s https://api.ipify.org |
| Resolve a name | dig +short example.com |
| Bypass my resolver | dig +short example.com @1.1.1.1 |
| Which resolver am I using | resolvectl status |
| Is that port open? | nc -zv host 443 |
| Where does it break? | mtr host |
Related reading
- Networking basics — the commands in practice
- Linux firewalls — what is dropping your packets
- Migrating a server — DNS TTLs and the hosts-file trick
- curl and wget — testing a service once you can reach it
- Tailscale — sidestepping NAT entirely
- Reading Linux logs — what the service says about the connection
