ip
Ask a Linux server what its own address is and you'll get an answer from one of two commands. ifconfig is the one most tutorials still show, and it's the one that has been unmaintained in the net-tools package for well over a decade. ip is the one that actually reflects what the kernel is doing — and on a minimal Ubuntu Server or a container image, it's frequently the only one installed.
That last point matters more than the philosophical argument about which is better. Exec into a Debian-based container and type ifconfig, and you'll usually get command not found. Type ip a and you get an answer.
One command, several objects
ip isn't a single tool with flags; it's a dispatcher. The word right after ip names the object you're asking about, and the word after that names the operation:
Four objects cover almost everything a DevOps engineer needs day to day:
| Object | What it manages | Most-used form |
|---|---|---|
link |
Layer 2 — the interface itself, its MAC address, its MTU, whether the cable is up | ip link show |
addr |
Layer 3 — the IP addresses bound to an interface | ip addr show |
route |
The routing table — where packets go next | ip route show |
neigh |
The ARP/NDP neighbour cache — which MAC belongs to which local IP | ip neigh show |
Every object name can be abbreviated to any unambiguous prefix, which is why you'll see ip a, ip r, and ip l in other people's notes and in your own muscle memory soon enough. show is the default operation, so ip addr and ip addr show are the same thing.
Reading ip addr line by line
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 84532sec preferred_lft 84532sec
inet6 fe80::5054:ff:fe12:3456/64 scope link
valid_lft forever preferred_lft forever
Your interface name, addresses, and MAC will differ. What each part means:
2:— the kernel's interface index. Not cosmetic:tcpdumpand packet-capture filters occasionally identify an interface by this number, and inside containers you'll see it referenced when tracing which host-side interface a container's virtual interface is paired with.<BROADCAST,MULTICAST,UP,LOWER_UP>— interface flags.UPmeans an administrator has enabled the interface.LOWER_UPmeans the physical layer has a signal — the cable is plugged in and the link partner answers. Those two are separate for a reason: an interface can beUPwith noLOWER_UP, which is exactly what an unplugged cable or a dead switch port looks like.mtu 1500— the largest payload this interface will send in one frame, the standard Ethernet value. A tunnel or VPN interface will show something smaller.state UP— the operational state, the summary of the two flags above.link/ether 52:54:00:…— the MAC address. The52:54:00prefix here is QEMU/KVM's, so this output is from a virtual machine.inet 192.168.1.10/24— the IPv4 address and its prefix length, together in one field. This is a genuine improvement overifconfig, which printed the address and the mask as two separate values you had to mentally recombine.scope global dynamic— reachable from anywhere (as opposed toscope host, which is loopback-only), and assigned by DHCP rather than configured statically.valid_lft 84532sec— how much of the DHCP lease remains. When someone insists a server has a static address and this line shows a countdown, they're wrong, and that's usually the whole bug.
When you only want the summary across every interface, the brief form fits on one line each:
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 192.168.1.10/24 fe80::5054:ff:fe12:3456/64
docker0 DOWN 172.17.0.1/16
-brief can be shortened to -br, and it accepts -c alongside it for colour. This is the form worth building the habit around — on a host with a dozen virtual interfaces, the full output scrolls off the screen and the brief form doesn't.
The routing table, and the one command that answers the real question
default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.10 metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10 metric 100
Two entries, and between them they decide the fate of every packet this host sends.
The second line is the directly connected route: everything in 192.168.1.0/24 is on the same wire, so the host resolves the destination's MAC address with ARP and sends the frame straight there. No router involved. proto kernel means nobody configured this route — the kernel created it automatically the moment an address with a /24 prefix was assigned.
The first line is the default route: anything that didn't match a more specific entry goes to 192.168.1.1, the router. A host with no default route can reach its own LAN perfectly and nothing else, which produces the confusing symptom of "ping 192.168.1.5 works but ping 8.8.8.8 says network is unreachable."
Reading the table and mentally running the longest-prefix match is fine for two routes. On a host with a VPN, a container bridge, and a second NIC it stops being fine, and there's a better command:
This asks the kernel to make the actual decision for one specific destination and report it: which gateway, out of which interface, and — the field people miss — which source address the packet will carry. src 192.168.1.10 is what the far end will see and what its firewall rules will match against. On a multi-homed host, "the firewall allows our office IP but the traffic arrives from the wrong address" is a real and common outage, and this one line diagnoses it.
ip route getis the routing command to reach for first. It replaces guesswork about which of six routes wins with the kernel's own answer.
Nothing here survives a reboot
ip talks to the running kernel. It does not write a configuration file, and it has no concept of one.
That address exists immediately and is gone after systemctl restart systemd-networkd or a reboot. Which is occasionally exactly what you want — a temporary second address to test a migration, removed with ip addr del when you're done — but it's a trap when you think you've configured a server. Persistent configuration on Ubuntu goes through netplan, and this course's Linux section covers that separately.
Changing routes or addresses over SSH can end the session that's running the command
sudo ip route del default or sudo ip addr flush dev eth0 on a remote host takes effect on the packet you're typing into. There is no confirmation prompt and no undo.
Before touching either, save the current state so you can put it back:
Then make the change only if one of these is true: you're in a lab VM with console access, you have out-of-band access (a cloud provider's serial console, IPMI), or you've queued an automatic rollback — for example sudo sh -c 'sleep 120; ip route add default via 192.168.1.1 dev eth0' & before the change, so the working route comes back on its own if you lock yourself out. Cancel the timer once you've confirmed the new configuration works.
A short investigation: the interface that's up but deaf
A monitoring alert says a database host stopped answering. SSH still works, because you're coming in over a second interface. Start at the bottom:
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
eth0 UP 52:54:00:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
eth1 DOWN 52:54:00:aa:bb:cc <NO-CARRIER,BROADCAST,MULTICAST,UP>
eth1 is where the database listens. Its flags say UP — an administrator enabled it — but NO-CARRIER, and LOWER_UP is absent. The kernel is willing; the physical layer isn't answering. That rules out addressing, routing, firewalls, and the database itself in one command, and points at a cable, a switch port, or a virtual NIC detached from its virtual switch.
Compare that with a different failure on the same interface:
Link is fine here. If the service is still unreachable, the next question is whether eth1 has an address at all (ip -br addr), and after that whether anything is listening on the port — which is ss's job, not ip's.
Interface counters can settle the "is it dropping traffic" question without a packet capture:
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:aa:bb:cc brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped missed mcast
184927362 204817 0 0 0 1204
TX: bytes packets errors dropped carrier collsns
37281940 152003 0 0 0 0
Errors and drops at zero means the NIC and its driver are not the problem, and you can stop looking there. Non-zero and climbing means they are. (The exact column headers shifted between iproute2 releases — older versions print overrun where newer ones print missed — so read the header row rather than counting columns.)
Practice
- Run
ip -br addrandifconfigon the same machine. List two pieces of information theipoutput gives you that theifconfigoutput doesn't. - Use
ip route getwith three destinations:127.0.0.1, another address on your own LAN, and a public address. Explain, for each, why the chosen interface and source address are what they are. - Look at
ip neigh show. Find one entry inSTALEstate and one inREACHABLE, and work out from the output alone which of those two machines this host has spoken to most recently. - On a lab VM only, add a second address to an interface with
ip addr add, confirm it withip -br addr, reboot, and confirm it's gone. Then explain in one sentence why a colleague's "I definitely set that IP" claim deserves avalid_lftcheck.
Exercise 3 hints at something ip deliberately doesn't answer. It shows you the machine's addresses, its routes, and its neighbours — the plumbing. It says nothing about which programs are attached to that plumbing, which port they claimed, or whether the port a client is failing to reach is even open. That's a different question and a different tool.
Sources
- Linux man-pages, ip(8)
- Linux man-pages, ip-route(8)
- Linux man-pages, ip-address(8)