Static Network Configuration with ip and nmcli
I configured persistent static IPv4 addressing, default routes, and DNS resolvers on Linux using both the runtime ip tool and persistent nmcli profiles. Static configuration removed DHCP lease dependency for infrastructure hosts that must hold deterministic addresses.
Objective & Context
Servers, gateways, and DNS hosts need fixed addresses that survive reboots and DHCP outages. This lab distinguishes the volatile runtime layer (ip) from the persistent layer (NetworkManager/systemd-networkd), so changes are both immediate and durable.
Learning Objectives
- Apply a runtime address, route, and DNS with the ip commands.
- Persist the same configuration with an nmcli connection profile.
- Distinguish volatile runtime state from persistent configuration.
- Reload and verify addressing after changes.
- Recover safely when a change breaks connectivity.
Environment & Prerequisites
Prerequisites
- Ubuntu 22.04 / RHEL 9 with NetworkManager 1.4x.
- An assigned subnet, gateway, and DNS resolver addresses.
- Console access in case a change drops connectivity.
- Ubuntu 22.04 or RHEL 9 with NetworkManager 1.4x.
- Assigned subnet, gateway, and DNS resolver addresses.
- Console access in case a change drops connectivity.
- Sudo access for ip and nmcli changes.
- Understanding of CIDR notation.
Step-by-Step Execution
1. Apply a runtime address with ip [PRIVILEGED]
ip addr add 192.168.10.20/24 dev eth0 && ip route add default via 192.168.10.1Expected output:
(no output on success)
What this teaches: ip commands change the live kernel state instantly, which is perfect for testing and terrible for permanence.
2. Persist with an nmcli profile
nmcli con mod eth0 ipv4.addresses 192.168.10.20/24 ipv4.gateway 192.168.10.1 ipv4.dns 192.168.10.2 ipv4.method manualExpected output:
ipv4.method: manual
ipv4.addresses: 192.168.10.20/24
ipv4.gateway: 192.168.10.1
ipv4.dns: 192.168.10.2
What this teaches: NetworkManager profiles are the durable layer; the runtime state is rebuilt from them on every boot.
Note: nmcli con mod prints nothing on success. The property listing shown above is what nmcli con show eth0 | grep ipv4 displays afterwards. Also, Ubuntu Server 22.04 defaults to netplan with systemd-networkd, not NetworkManager: either install NetworkManager (sudo apt install -y network-manager) and let it manage the interface, or express the same static config in /etc/netplan/ and run sudo netplan apply.
3. Reload and verify
nmcli con up eth0 && ip -br addr show eth0Expected output:
eth0 UP 192.168.10.20/24
What this teaches: verification closes the loop between what you asked for and what the system actually applied.
How It Works
Linux networking has two layers that are easy to confuse. The runtime layer is the kernel's live state: addresses on interfaces, routes in tables, neighbor entries. The ip commands manipulate this layer directly, and changes take effect immediately but vanish on reboot.
The persistent layer is whatever recreates the runtime state at boot: NetworkManager profiles, systemd-networkd files, or netplan YAML. nmcli edits the profile on disk; nothing changes on the wire until the profile is activated with con up, at which point NetworkManager rewrites the runtime state to match.
DNS resolution sits slightly apart: /etc/resolv.conf is the runtime view, often managed by systemd-resolved or NetworkManager. Setting DNS in the nmcli profile ensures the resolver configuration is rebuilt correctly alongside the address, which is why all three (address, gateway, DNS) belong in one profile.
Validation & Testing
Reboot and confirm the address persists, then test gateway reachability and DNS resolution. Pass criteria: identical address after reboot, successful ping to the gateway, and name resolution via the configured resolver.
ping -c2 192.168.10.1 && dig +short tyfsadik.orgExpected output:
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.7 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.6 ms
--- 192.168.10.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss
203.0.113.10
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Address applied but lost after reboot | Only the runtime layer was changed; no persistent profile exists | Create or modify the nmcli profile and activate it |
| nmcli profile exists but never activates | autoconnect disabled or the profile is bound to the wrong interface | Check nmcli con show and confirm connection.autoconnect is yes |
| Can ping the gateway but not the internet | Missing or wrong default route | Verify ip route show has a default via the gateway |
| DNS fails despite correct IP and gateway | resolv.conf not updated or pointing at an unreachable resolver | Check resolvectl status and confirm the profile's ipv4.dns took effect |
| Locked out after changing the address remotely | The new address never applied correctly and the old one is gone | Use console access; for remote changes, schedule a revert with shutdown -r +5 before editing |
Advanced: Troubleshooting
- Address lost on reboot: the runtime
ipchange is not persistent; commit it in nmcli. - No DNS: verify
/etc/resolv.confis managed and points to the configured resolver. - Two managers fighting: run either NetworkManager or systemd-networkd, not both, on the same NIC.
Key Results
- Delivered deterministic addressing on infrastructure hosts with 0 DHCP dependency.
- Persisted configuration surviving 5/5 reboot cycles.
- Cut DHCP-related address-change incidents to zero for fixed servers.
- Standardized one nmcli profile template across managed nodes.
Key Takeaways
- Runtime changes are for testing; profiles are for keeping.
- Always verify with ip addr and ip route after activating a profile.
- Keep console access handy when changing addresses remotely.
- Address, gateway, and DNS belong together in one persistent profile.
- IPv6 needs its own addressing plan; disabling it is rarely the right answer.
- Record the change in your IPAM so the next person trusts the address.
Practice Challenges
Challenge 1: Add a second address
Goal: Assign a secondary IP to the same interface via nmcli and prove both respond to ping.
Hint: Add a second value to ipv4.addresses separated by a comma.
Challenge 2: Convert to systemd-networkd
Goal: Reproduce the same static config with a systemd-networkd .network file and disable NetworkManager for the interface.
Hint: Write /etc/systemd/network/10-eth0.network with Address=, Gateway=, and DNS= lines.