Network configuration on Linux — theory with ip, ifcfg and resolv.conf (and the IP that vanished on reboot)
In the second networks lab I got my workstation pinging the gateway at the first attempt. ip addr add, ip route add, ping, all green. I switched the computer off feeling pleased. The following week I switched it on, pinged… nothing: the card had no IP. Everything I’d configured had evaporated. The lesson: ip commands change the network now, in memory; whatever you want to survive a reboot goes in a configuration file.
In the vi article we edited ifcfg-enp2s0 without fully knowing what each line meant. Today we find out: how to look at a Linux machine’s network, how to configure it on the spot with ip and how to make it stick with ifcfg and /etc/resolv.conf. Then, in the practice, we set up a whole workstation.
Table of Contents
This topic’s lab
Reconfiguring your own computer’s network to run tests is the fastest way to lose your internet connection. That’s why everything in this article was run in an isolated virtual lab: three machines simulated inside one Linux with network namespaces (each has its own network stack) joined by virtual cables (veth). It reproduces the IC2 lab setup: a workstation, a gateway and the lab router.
The workstation is desk 7: its network is 10.110.7.0/24 and its IP will be 10.110.7.4. The gateway has two cards, one on each network, and forwards packets from one to the other. Every output you’ll see is real, from a single run.
Since the cards are virtual, the outputs show two details you won’t see in the lab: an @if49 suffix (the number of the card at the other end of the cable) and link-netns gateway (which machine that other end is on). Commands are run as root (# prompt): changing the network requires being an administrator. Your ping times and card numbers will differ.
Four ideas before typing
- Interface (card): the machine’s door to a network.
lois special: the loopback, the machine itself (127.0.0.1). - IP address and mask:
10.110.7.4/24says “I’m 10.110.7.4 and my network is the addresses that share the first 24 bits”./24is the same as the mask255.255.255.0: 8 + 8 + 8 + 0 = 24 bits set. My network runs from10.110.7.0to10.110.7.255. - Default gateway: the machine on my network I send everything that isn’t on my network to. Here, the gateway
10.110.7.1. - DNS: the server that translates names into IPs. It’s configured in
/etc/resolv.conf.
How cards are named
Cards used to be called eth0, eth1 (wired) and wlan0 (wireless). The problem: the number depended on the order the system detected them at boot, and today’s eth1 could be eth0 tomorrow. Now the name says what it is and where it is:
| Part | Means | Example |
|---|---|---|
en / wl | Ethernet (cable) / WLAN (wifi) | |
p<bus>s<slot> | physical position on the PCI bus | enp2s0: Ethernet, bus 2, slot 0 |
o<index> | built into the motherboard | eno1 |
lo | loopback, the machine itself |
So wlp9s0 is the wifi on bus 9, slot 0. The name doesn’t change from one boot to the next.
The ip command: looking at the network
The modern command is ip (from the iproute2 package). Its form is:
ip [options] object command
The object is what you’re talking about: link (the cards), address (the IPs), route (the routes). It can be shortened: address, addr and a are the same. If you give no command, show is assumed. Options go before the object: -br (brief, one line per card), -s (statistics), -c (colours). And ip address help shows the help for each object.
This is the workstation just switched on, with nothing configured:
# @lab base # A workstation just switched on: the card exists, but it's down and has no IP ip link # link object: the cards and their state ip addr # address object: the same plus the IPs ip -br addr # -br (brief): one line per card ip route # the routing table: empty
The real session:
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
50: enp2s0@if49: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 08:00:27:a1:07:04 brd ff:ff:ff:ff:ff:ff link-netns gateway
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
50: enp2s0@if49: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 08:00:27:a1:07:04 brd ff:ff:ff:ff:ff:ff link-netns gateway
# ip -br addr
lo UNKNOWN 127.0.0.1/8
enp2s0@if49 DOWN
# ip route
Let’s read it piece by piece:
ip linkshows two cards:loandenp2s0. The network card hasstate DOWNandUPis missing from its flags: it’s switched off.ip addrrepeats the same and adds the IPs:lohas127.0.0.1/8,enp2s0has none (there’s noinetline).ip -br addrsums it up as a table: name, state, addresses.ip routeprints nothing: the routing table is empty. This machine doesn’t know how to get anywhere.
ip -br addr is the command you’ll use most. In one line you see whether the card is UP and what IP it has.
Configuring on the spot: link, addr and route
For the workstation to talk to the gateway it takes three steps: bring the card up, give it an IP with a mask and, to leave its network, a default gateway.
# @lab base ip link set enp2s0 up # bring the card up ip addr add 10.110.7.4/24 dev enp2s0 # IP and mask (/24 = 255.255.255.0) ip addr show enp2s0 # just that card ip route # the kernel has added a route by itself ping -c 3 10.110.7.1 # the gateway, on my own network ping -c 1 10.110.1.1 # the router, on ANOTHER network ip route add default via 10.110.7.1 # default gateway: anything not on my network goes to the gateway ip route ping -c 3 10.110.1.1 # now it works
The real session:
# ip link set enp2s0 up
# ip addr add 10.110.7.4/24 dev enp2s0
# ip addr show enp2s0
54: enp2s0@if53: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 08:00:27:a1:07:04 brd ff:ff:ff:ff:ff:ff link-netns gateway
inet 10.110.7.4/24 scope global enp2s0
valid_lft forever preferred_lft forever
# ip route
10.110.7.0/24 dev enp2s0 proto kernel scope link src 10.110.7.4
# ping -c 3 10.110.7.1
PING 10.110.7.1 (10.110.7.1) 56(84) bytes of data.
64 bytes from 10.110.7.1: icmp_seq=1 ttl=64 time=0.342 ms
64 bytes from 10.110.7.1: icmp_seq=2 ttl=64 time=0.064 ms
64 bytes from 10.110.7.1: icmp_seq=3 ttl=64 time=0.043 ms
--- 10.110.7.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2050ms
rtt min/avg/max/mdev = 0.043/0.149/0.342/0.136 ms
# ping -c 1 10.110.1.1
ping: connect: Network is unreachable
# ip route add default via 10.110.7.1
# ip route
default via 10.110.7.1 dev enp2s0
10.110.7.0/24 dev enp2s0 proto kernel scope link src 10.110.7.4
# ping -c 3 10.110.1.1
PING 10.110.1.1 (10.110.1.1) 56(84) bytes of data.
64 bytes from 10.110.1.1: icmp_seq=1 ttl=63 time=0.386 ms
64 bytes from 10.110.1.1: icmp_seq=2 ttl=63 time=0.067 ms
64 bytes from 10.110.1.1: icmp_seq=3 ttl=63 time=0.072 ms
--- 10.110.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2039ms
rtt min/avg/max/mdev = 0.067/0.175/0.386/0.149 ms
Line by line:
ip link set enp2s0 upprints nothing (in Linux, silence means success), but the card now hasUP,LOWER_UP: switched on and with a cable.ip addr add 10.110.7.4/24 dev enp2s0adds the IP.ip addr show enp2s0shows it on theinetline.ip routeis no longer empty: the kernel has added the route10.110.7.0/24 dev enp2s0by itself. Told “I’m .4 of a /24”, it understands that the 256 addresses of that network are reached directly throughenp2s0.ping -c 3 10.110.7.1(3 packets) works: the gateway is on my network.ttl=64is Linux’s typical starting value: the packet hasn’t crossed any router.ping -c 1 10.110.1.1fails withNetwork is unreachable: the router is on another network and the table has no route that covers it. The ping doesn’t even get to send anything.ip route add default via 10.110.7.1adds the default route: “anything you don’t know where to send, send to the gateway”.- Now the router replies, with
ttl=63: 64 − 1, because the packet went through a router (the gateway) on the way.
Here is the output of ip addr show taken apart:
Notice our inet line has no brd (broadcast address). ip only adds it if you ask with brd +, which works it out from the mask (you’ll see it in the exercises).
| I want to… | Command |
|---|---|
| bring the card up / down | ip link set enp2s0 up / down |
change the MAC (with the card down) | ip link set enp2s0 address 12:34:56:78:90:ab |
| add / remove an IP | ip addr add 10.110.7.4/24 dev enp2s0 / ip addr del … |
| remove every IP from the card | ip addr flush dev enp2s0 |
| add / remove the default gateway | ip route add default via 10.110.7.1 / ip route del default |
| see which path a packet would take | ip route get 10.110.1.1 |
Forgetting the /24. ip addr add 10.110.7.4 dev enp2s0 sets /32: a network of a single address. The network route isn’t created and not even the gateway replies. You’ll see it live in the exercises.
Undoing: removing the IP and bringing the card down
Knowing what happens when you remove things matters just as much. We start from the configured workstation:
# @lab conf ip addr del 10.110.7.4/24 dev enp2s0 # remove the IP ip route # the routes that depended on it go too ip addr add 10.110.7.4/24 dev enp2s0 # we put it back ip link set enp2s0 down # and now we bring the card down ip -br addr # the IP is still there, but the card is DOWN ip route ping -c 1 10.110.7.1
The real session:
# ip addr del 10.110.7.4/24 dev enp2s0 # ip route # ip addr add 10.110.7.4/24 dev enp2s0 # ip link set enp2s0 down # ip -br addr lo UNKNOWN 127.0.0.1/8 enp2s0@if61 DOWN 10.110.7.4/24 # ip route # ping -c 1 10.110.7.1 ping: connect: Network is unreachable
Two surprising things:
- When you remove the IP with
ip addr del,ip routeends up empty: the network route and the default route depended on that IP and the kernel deletes them with it. If you put the IP back, the default gateway has to be added again. - When you bring the card down with
ip link set enp2s0 down, the IP stays (ip -br addrshows it next toDOWN), but the routes disappear and ping fails withNetwork is unreachable. A card that’s down is like an unplugged cable.
ifconfig and route: the old commands
In plenty of notes, tutorials and older machines you’ll see ifconfig and route (net-tools package). They’re obsolete, but worth being able to read. With the workstation configured:
# @lab conf # The old commands (net-tools package) still work on many machines ifconfig enp2s0 # the same as ip addr show enp2s0, in another format route -n # the same as ip route, as a table
The real session:
# ifconfig enp2s0
enp2s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.110.7.4 netmask 255.255.255.0 broadcast 0.0.0.0
ether 08:00:27:a1:07:04 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.110.7.1 0.0.0.0 UG 0 0 0 enp2s0
10.110.7.0 0.0.0.0 255.255.255.0 U 0 0 0 enp2s0
ifconfig gives the same information in another format: inet and netmask separately (the mask written out in full, 255.255.255.0), ether for the MAC, and packet counters. The broadcast 0.0.0.0 is what we mentioned: we didn’t ask for a broadcast address. route -n shows the routing table: the 0.0.0.0 line with Gateway 10.110.7.1 and the G flag is the default route; the 10.110.7.0 line with U and no gateway is the directly connected network. -n asks for numbers instead of names.
| net-tools (old) | iproute2 (current) |
|---|---|
ifconfig | ip addr (ip a) |
ifconfig enp2s0 up | ip link set enp2s0 up |
ifconfig enp2s0 192.168.0.2 netmask 255.255.255.0 | ip addr add 192.168.0.2/24 dev enp2s0 |
route -n | ip route |
route add default gw 10.110.7.1 | ip route add default via 10.110.7.1 |
Temporary versus permanent
Everything we’ve done with ip lives in the kernel’s memory: it works instantly and disappears on reboot (the story at the start). To make the configuration permanent it’s written to files the system reads at boot:
On CentOS (the system used in the labs), each card has its own file /etc/sysconfig/network-scripts/ifcfg-<name>. This is our workstation’s, with the values we just tested by hand:
# @lab conf cat ifcfg-enp2s0 # the permanent configuration (on CentOS, in /etc/sysconfig/network-scripts/) cat /etc/resolv.conf # the DNS servers that get asked cat /etc/hosts # names resolved without asking anyone ping -c 2 router # a name instead of an IP
The real session:
# cat ifcfg-enp2s0 DEVICE=enp2s0 BOOTPROTO=none ONBOOT=yes IPADDR=10.110.7.4 NETMASK=255.255.255.0 GATEWAY=10.110.7.1 # cat /etc/resolv.conf nameserver 193.145.147.22 nameserver 193.145.138.100 nameserver 193.145.138.200 # cat /etc/hosts 127.0.0.1 localhost 10.110.7.4 workstation 10.110.7.1 gateway 10.110.1.1 router # ping -c 2 router PING router (10.110.1.1) 56(84) bytes of data. 64 bytes from router (10.110.1.1): icmp_seq=1 ttl=63 time=0.372 ms 64 bytes from router (10.110.1.1): icmp_seq=2 ttl=63 time=0.065 ms --- router ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1025ms rtt min/avg/max/mdev = 0.065/0.218/0.372/0.153 ms
The ifcfg keys:
| Key | What it says | Values |
|---|---|---|
DEVICE | the card’s name | enp2s0 |
BOOTPROTO | where the IP comes from | none (the one in the file), dhcp (a server hands it out), bootp |
ONBOOT | whether it’s brought up at boot | yes / no |
IPADDR | the IP | 10.110.7.4 |
NETMASK | the mask (or PREFIX=24) | 255.255.255.0 |
GATEWAY | the default gateway | 10.110.7.1 |
NETWORK | the network address (optional) | 10.110.7.0 |
After editing the file (with vi, of course), you apply it by restarting the network service: systemctl restart network. In the lab, NetworkManager is also stopped and disabled (systemctl stop NetworkManager and systemctl disable NetworkManager) so it doesn’t override the configuration in the files.
This article’s lab isn’t CentOS, so systemctl restart network wasn’t run: the file is shown as text. In the practice we write a script that does exactly what that service does with each ifcfg, and we really run it.
The other two files in the session:
/etc/resolv.conf: onenameserverline per DNS server, tried in order. These are the university’s./etc/hosts: names the machine resolves on its own, without asking any DNS. That’s whyping -c 2 routerworks: the system checks/etc/hostsfirst, finds10.110.1.1 routerand pings that IP (the output saysPING router (10.110.1.1)).
ping: proof there’s a network
ping sends ICMP “echo” packets and waits for the reply. What to look at:
| In the output | Means |
|---|---|
64 bytes from 10.110.7.1: icmp_seq=1 ttl=64 time=0.342 ms | it replied; ttl drops by 1 for every router crossed; time is the round trip |
3 packets transmitted, 3 received, 0% packet loss | the summary: none were lost here |
connect: Network is unreachable | no route: the IP, the mask or the default gateway is missing |
Destination Host Unreachable | there’s a route, but the machine the packet must be handed to on your local network doesn’t answer (or doesn’t exist) |
On Linux, ping doesn’t stop by itself: use -c N to send N packets, or Ctrl-C to stop it.
Common mistakes
ip addr add 10.110.7.4 dev enp2s0 sets /32. Always write /24 (or whatever prefix applies).
The IP is set correctly but the card is still DOWN. First ip link set enp2s0 up.
The gateway has to be directly reachable. ip route add default via 10.110.1.1 from the workstation gives Error: Nexthop has invalid gateway.
What you do with ip vanishes on reboot. Once it works, move it to the ifcfg and set ONBOOT=yes.
Commands that change something give Operation not permitted. Looking, you can do as a normal user.
Try it in your terminal
These commands are safe: they only look, and you can run them on your own Linux without worry. This is how they came out on the workstation, as a normal user ($ prompt):
# @lab conf # @sergio ip -br addr # looking doesn't need root ip route ping -c 3 127.0.0.1 # lo: the machine itself ip addr add 10.110.7.9/24 dev enp2s0 # changing does
The real session:
$ ip -br addr lo UNKNOWN 127.0.0.1/8 enp2s0@if69 UP 10.110.7.4/24 $ ip route default via 10.110.7.1 dev enp2s0 10.110.7.0/24 dev enp2s0 proto kernel scope link src 10.110.7.4 $ ping -c 3 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.233 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.043 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.050 ms --- 127.0.0.1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2044ms rtt min/avg/max/mdev = 0.043/0.108/0.233/0.087 ms $ ip addr add 10.110.7.9/24 dev enp2s0 RTNETLINK answers: Operation not permitted
What to watch for:
ip -br addrandip routework without root: querying the network needs no permissions. On your machine you’ll see your own cards (probably awlp…one if you’re on wifi).- In your
ip route, thedefault via …line is your home or university router: your default gateway. ping -c 3 127.0.0.1always works if the network stack is alive: it’s the machine itself, throughlo. The times are tiny.- The last command tries to change something and the kernel refuses:
RTNETLINK answers: Operation not permitted. Don’t run it withsudoon your computer unless you want to really change your network. - Also try
cat /etc/resolv.conf: on many desktop Linux systems you’ll seenameserver 127.0.0.53, a local middleman (systemd-resolved) that forwards queries to the real DNS servers.
Summary
| Concept | The essentials |
|---|---|
| Names | enp2s0 = Ethernet, bus 2, slot 0; wl… wifi; lo the machine itself |
| Looking | ip -br addr, ip link, ip route, ip route get IP |
| Configuring on the spot | ip link set X up, ip addr add IP/24 dev X, ip route add default via GW |
| Undoing | ip addr del, ip addr flush dev X, ip route del default, ip link set X down |
| Old ones | ifconfig, route -n (net-tools, obsolete) |
| Permanent | ifcfg-X (BOOTPROTO, ONBOOT, IPADDR, NETMASK, GATEWAY) + systemctl restart network; DNS in /etc/resolv.conf |
| Checking | ping -c N: ttl, % packet loss, Network is unreachable |
Three ideas to take away:
- Three steps to get a network: card
up, IP with a mask, default gateway (on your network). ipchanges the network instantly and in memory; theifcfgwithONBOOT=yesmakes it stick.- When something fails, check in order:
ip -br addr(UP? IP/prefix?),ip route(default?),pingthe gateway and then further away.
In the network configuration practice we bring the workstation up step by step, fix a misconfigured one and turn the ifcfg into commands with a script. This topic closes the practical Linux part of IC2; next comes the networking theory behind all of this.
Did this article help you? Here is how you can support SergioLearns →




What is an operating system — theory with your laptop on a study afternoon, a write() call and a CPU that never rests (and the three programs that weren’t running at the same time)