, ,

Linux Networking From the Command Line: ip, ss, and ping (Linux for Beginners, Part 12)

Ping the address and it answers, but the name will not resolve. This is Linux networking from the command line: ip, ss, ping and dig, what each one proves, and how to make the config stick on Debian and RHEL.

Linux for Beginners · Part 12 of 24

The site is down. That is the message that woke you up. You SSH into the box, the shell answers instantly, and the first command you run tells a different story.

$ curl -I https://api.internal.example
curl: (6) Could not resolve host: api.internal.example

$ ping -c1 1.1.1.1
64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=8.42 ms
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss

Read those two results together. Packets reach a public address and come back in eight milliseconds, so the machine has a working path to the internet. What it cannot do is turn a name into an address. Nothing is down. DNS is broken, and that one distinction is most of network troubleshooting on Linux. The rest of this part is the small set of commands that let you tell those two failures apart in under a minute.

Start here. If you just finished college, treat this as the map nobody drew for you: how a request gets from your shell to another machine, and the one command that inspects each hop. If you already run servers, this is the fast reference for the iproute2 tools that replaced ifconfig and netstat, plus the split between Debian and RHEL on making the config survive a reboot.
How a request leaves your machineYour shellcurl, sshYour NICip addrGatewayip routeDNS resolverdigRemote hostpingEvery box is a place the request can fail, and every box has one command that inspects it.

What your machine already knows about the network

Before you test anything remote, ask the box what it thinks its own situation is. Two questions cover most of it: what address do I have, and where do I send packets that are not local. The ip command answers both.

$ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
    link/ether 52:54:00:a1:b2:c3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.24/24 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 84150sec preferred_lft 84150sec

Reading that output

The word UP in the angle brackets means the kernel has the interface administratively enabled. LOWER_UP means the cable or virtual link actually carries a signal. You can have UP without LOWER_UP, and that combination is a dead cable or a downed switch port. The line that matters most is inet 192.168.1.24/24, which is your address and the size of your local network in one token. The word dynamic tells you a DHCP server handed this out, so it can change on the next lease.

The default route is the line to check first

$ ip route
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.24

The first line is the whole reason a packet to a public address knows where to go. Anything that is not on 192.168.1.0/24 gets handed to the gateway at 192.168.1.1. No default route means the box can talk to its own subnet and nothing else, which looks exactly like a firewall problem until you run this one command. If you want to know which interface and gateway a specific destination would use, ip route get tells you without sending a single packet:

$ ip route get 1.1.1.1
1.1.1.1 via 192.168.1.1 dev eth0 src 192.168.1.24 uid 1000
    cache
What the /24 in 192.168.1.24/24 means24 network bits (fixed)8 host bitsSame first three octets means same network. That leaves 254 usable host addresses.

Is it reachable, and what ping actually proves

ping sends an ICMP echo request and waits for an echo reply. When it works, you have proven exactly one thing: packets can travel to that address and back at the network layer. The ttl value tells you roughly how many routers the reply crossed, since each hop decrements it. The time value is the round trip in milliseconds.

Here is where I disagree with the advice you will hear on day one. People say ping tests whether a server is up. It does not. A server can serve traffic on port 443 all day while dropping every ICMP packet at its firewall, so your ping times out against a perfectly healthy machine. The reverse is worse: ping succeeds, you declare victory, and the application is still down because the service crashed. Ping tests the road, not the shop at the end of it. Treat a successful ping as proof of a route and nothing more.

DNS is the layer that breaks the most

Go back to the opener. The address pinged fine, the name failed. That pattern points at name resolution, and on Linux the tool that shows you the truth is dig. On Debian and Ubuntu it comes from the dnsutils package, on the RHEL family from bind-utils. Ask it for a name and watch what comes back.

$ dig +short api.internal.example
$ echo $?
0

$ dig api.internal.example | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 34122

An empty answer with an exit code of 0 is the trap. The lookup ran cleanly, the resolver answered, and the answer was that the name does not exist. That is the NXDOMAIN status in the full output. So now the question is which resolver gave that answer. Two files and one command tell you.

$ cat /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trailing
search internal.example

$ resolvectl status | grep 'DNS Servers'
       DNS Servers: 10.0.0.2 10.0.0.3

On most current systemd distributions /etc/resolv.conf points at 127.0.0.53, which is the local stub resolver run by systemd-resolved. That stub forwards to the real servers, and resolvectl status is how you see who they are. If those upstream servers are wrong or unreachable, every name fails while every address still works. That is precisely the shape of the failure in the opener. The fix was not restarting the app. It was pointing systemd-resolved at a DNS server that actually held the internal zone.

Where this bites in production. A team edits /etc/resolv.conf by hand to fix DNS, it works, everyone moves on. On the next reboot the change is gone, because on a systemd-resolved box that file is a generated symlink and gets rewritten. Names break again and nobody connects it to the reboot. If you need a permanent resolver, set it where the generator reads it, through netplan or NetworkManager, not by editing the file the generator overwrites.

What is listening, and what is connected

When ping succeeds but the service does not answer, the next question is whether anything is even listening on the port. ss is the tool. It reads socket state straight from the kernel over netlink, which makes it faster than the old netstat it replaced. The flags worth memorizing are -t for TCP, -l for listening, -n for numeric ports, and -p for the owning process.

$ sudo ss -tlnp
State   Recv-Q  Send-Q   Local Address:Port   Peer Address:Port  Process
LISTEN  0       4096     127.0.0.53%lo:53          0.0.0.0:*      users:(("systemd-resolve",pid=612,fd=14))
LISTEN  0       128            0.0.0.0:22          0.0.0.0:*      users:(("sshd",pid=901,fd=3))
LISTEN  0       511            127.0.0.1:8080       0.0.0.0:*      users:(("node",pid=1440,fd=18))

Look at the last line and the reason for a whole class of tickets appears. The Node app listens on 127.0.0.1:8080, the loopback address only. It answers requests from the same machine and refuses everything from outside, so a teammate on another host gets connection refused while a local curl works. The address in that column is not decoration. 0.0.0.0 means every interface, 127.0.0.1 means this machine only.

Sometimes packets do leave the box but stall somewhere between you and the destination. A single ping cannot tell you which hop is at fault, and that is where mtr earns its place. It merges ping and traceroute into one live table, sending probes to every router along the path and updating loss and latency for each hop in real time. The trick to reading it is to ignore loss that appears at one hop and then clears at the hops after it, because that is a router deprioritizing its own replies rather than dropping your traffic. Real trouble is loss that starts at a hop and continues all the way to the destination. On both Debian and RHEL the package is simply mtr, and mtr 1.1.1.1 is the whole command.

If your fingers still type the net-tools commands, here is the direct translation. These are the ones you will actually use.

What you wantOld (net-tools)Modern (iproute2)
Show addressesifconfigip addr
Show routesroute -nip route
Listening portsnetstat -tlnpss -tlnp
ARP neighboursarp -nip neigh
Interface countersnetstat -iip -s link

Making a change survive a reboot

Everything above reads state, and ip can also set it. The catch is that ip addr add and ip route add live only in the running kernel. Reboot and they vanish. For permanent config the two big families diverge, and this is the split that trips people moving between them.

Debian / Ubuntu ServerRHEL family (9 and later)
Config toolnetplan (YAML)NetworkManager via nmcli
Where it lives/etc/netplan/*.yaml/etc/NetworkManager/system-connections/
Apply itsudo netplan applysudo nmcli con up eth0
Backendsystemd-networkdNetworkManager

A static address on Ubuntu Server is a short YAML file. Mind the spaces, because netplan is strict about indentation and rejects tabs.

# /etc/netplan/01-static.yaml
network:
  version: 2
  ethernets:
    eth0:
      addresses: [192.168.1.24/24]
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [10.0.0.2, 10.0.0.3]

$ sudo netplan apply

The same result on RHEL 9 is a sequence of nmcli commands. RHEL 9 dropped the old ifcfg files, so this is the supported path now.

$ sudo nmcli con mod eth0 ipv4.addresses 192.168.1.24/24
$ sudo nmcli con mod eth0 ipv4.gateway 192.168.1.1
$ sudo nmcli con mod eth0 ipv4.dns "10.0.0.2 10.0.0.3"
$ sudo nmcli con mod eth0 ipv4.method manual
$ sudo nmcli con up eth0
Interview question you will actually get. The interviewer says: a user reports the application is down, but you can ping the server fine from your laptop. Walk me through your next three checks. A good answer moves up the stack without guessing. First ss -tlnp on the server to confirm the service is listening and on which address, since a bind to 127.0.0.1 explains the whole thing. Then check the listen address against the client, loopback only versus 0.0.0.0. Then dig the name from the client, because ping by IP hides a DNS failure. You are showing that ping proved the route and nothing else.
Check connectivity in this order1. Is the link up?ip link show eth02. Do I have an address?ip addr3. Is the gateway reachable?ping the gateway4. Does the name resolve?dig the name5. Is the port open?ss -tlnp
Try this now. On any Linux box you have, run ip route get 1.1.1.1. It prints the exact interface, source address, and gateway the kernel would use to reach that destination, and it sends nothing on the wire. Then run sudo ss -tlnp and find one service bound to 127.0.0.1. That is a service only this machine can reach. Verify your read with curl from the box and then from another host: the second one gets connection refused.

A worked example on a broken box

Theory sticks once you watch the ladder catch a real fault. A box cannot reach anything outside itself. Walk down the checks in order and let the errors tell you where to stop.

$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP

$ ip addr show eth0 | grep inet
    inet 10.20.0.15/24 brd 10.20.0.255 scope global eth0

$ ping -c1 10.20.0.1
64 bytes from 10.20.0.1: icmp_seq=1 ttl=64 time=0.31 ms

$ ping -c1 1.1.1.1
ping: connect: Network is unreachable

The link is up, the box has an address, and the gateway answers in a third of a millisecond, so the local network is fine. Then the public address fails, and the exact wording is the clue. It does not time out. It says Network is unreachable, which is the kernel refusing to send the packet at all because it has nowhere to send it. That verdict comes back instantly and locally. A timeout, by contrast, means the packet left and nothing answered. Confirm the cause with one command.

$ ip route
10.20.0.0/24 dev eth0 proto kernel scope link src 10.20.0.15

$ sudo ip route add default via 10.20.0.1
$ ping -c1 1.1.1.1
64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=9.62 ms

There is no default line in the route table, so every non-local packet has nowhere to go. Adding the default route fixes it on the spot. One warning: that ip route add lives in the running kernel only and disappears on reboot. Once you have proven the fix, write it into netplan or NetworkManager so it survives. The lesson is to read the error text before you touch anything, because Network is unreachable and Connection timed out send you to two different parts of the stack.

Common questions

Why does my new server say ifconfig: command not found? Because net-tools is not installed. Since RHEL 7, Red Hat stopped shipping it by default, and minimal Debian and Ubuntu images leave it out too. The tools are still there under different names. Use ip addr instead of ifconfig, and ss instead of netstat. Do not install net-tools out of habit.

Ping works but the website times out. What now? Ping only proved the route. Move up the stack. Check whether the service is listening with ss -tlnp, check the name with dig, and only then suspect the network. Most of the time the answer is a service bound to the wrong address or a DNS record pointing somewhere stale.

Do I really need both netplan and nmcli? You need the one your distribution uses. Ubuntu Server means netplan, RHEL and its rebuilds mean NetworkManager and nmcli. Learn your primary platform cold and keep a one page note on the other. The concepts of address, gateway, and DNS are identical, only the file and the command change.

Is netstat gone for good? It still works where net-tools is installed, but it is not the tool to learn now. ss gives the same information faster and is present on modern minimal installs where netstat is missing. Build the habit around ss.

Where this fits

Four commands carry most of your networking days: ip for addresses and routes, ping for reachability, dig for names, and ss for sockets. Run them in the order the ladder above shows and you will stop guessing. The next part gets you onto remote machines the right way, with ssh and keys, which is the skill you will use every single day. The same layered thinking shows up one level higher in cloud virtual networks, covered in the Cloud for Beginners series, and in datacenter networking in the VMware for Beginners guide. Open a terminal and run ip route get 1.1.1.1 right now, before you read on.

Linux for Beginners · Part 12 of 24
« Previous: Part 11  |  Complete Guide  |  Next: Part 13

References

Task-centered iproute2 user guide
Netplan documentation
Red Hat Enterprise Linux 9: Configuring and managing networking
dig(1) manual page

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading