RHEL Intermediate #4: Networking — NetworkManager (nmcli), bonding, teaming
The networking concepts we briefly touched during setup in Basics #2 get unpacked in earnest here. The network of RHEL 9 has been organized into NetworkManager alone, and this post looks at how to handle that configuration in actual operations.
The position of this post in the RHEL Intermediate series:
- #1 Intro to SELinux — Enforcing/Permissive, labels, troubleshooting
- #2 LVM — PV/VG/LV, snapshots, expansion
- #3 Advanced storage — Stratis, NFS, Samba
- #4 Networking — NetworkManager (nmcli), bonding, teaming ← this post
- #5 Log management — journald, rsyslog, log rotation
- #6 Job scheduling — cron, systemd timer, at
- #7 Intro to containers — Podman/Buildah/Skopeo (differences from Docker)
Why NetworkManager became the standard #
Old RHEL wrote network configuration in /etc/sysconfig/network-scripts/ifcfg-* files and applied with service network restart. It worked well for simple roles but was weak at wireless, VPN, dynamic IP, and simultaneous handling of multiple interfaces.
NetworkManager is a tool that integrated all of this into a single daemon. It operates with the same model on desktops, servers, laptops, and containers, changes are reflected immediately (no restart required), and the backend (D-Bus) and client tools (nmcli / nmtui / GUI) are diverse, making automation easy too.
user tools: nmcli (CLI) nmtui (TUI) GNOME (GUI) D-Bus
│ │ │ │
└─────────────┴──────┬───────┴────────────┘
▼
┌────────────────────┐
│ NetworkManager │ ← daemon
│ (NM) │
└─────────┬──────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
[Device] [Connection] [Routing/DNS]
actual NIC config profile /etc/resolv.conf
(enp0s1...) (Wired-1, vpn-...) /etc/hostsTwo key concepts:
- Device — actual NIC. Names like
enp0s1,wlan0. - Connection — a configuration profile to apply to a Device. You can create multiple connections for one Device and pick one to activate depending on the situation.
Keeping these two concepts distinct is the core of NetworkManager’s model. Always be clear about “which configuration is currently active on this NIC”.
nmcli — daily command group #
nmcli (NetworkManager CLI) is the starting point of nearly all work. It takes the form of attaching commands to three objects (general / device / connection).
$ nmcli
enp0s1: connected to Wired connection 1
"Virtio Network Device"
ethernet (virtio_net), 52:54:00:..., hw, mtu 1500
ip4 default
inet4 192.168.64.15/24
route4 default via 192.168.64.1 metric 100
inet6 fe80::.../64
route6 fe80::/64 metric 256
lo: unmanaged
DNS configuration:
servers: 192.168.64.1
interfaces: enp0s1Just typing nmcli shows everything on one screen. In operations, this is the first command you reach for when logging into a machine to check the network state.
Viewing devices #
$ nmcli device status
DEVICE TYPE STATE CONNECTION
enp0s1 ethernet connected Wired connection 1
lo loopback unmanaged --
$ nmcli device show enp0s1 # detail of one device
GENERAL.DEVICE: enp0s1
GENERAL.TYPE: ethernet
GENERAL.HWADDR: 52:54:00:...
GENERAL.MTU: 1500
GENERAL.STATE: 100 (connected)
...
IP4.ADDRESS[1]: 192.168.64.15/24
IP4.GATEWAY: 192.168.64.1
IP4.DNS[1]: 192.168.64.1Viewing connections #
$ nmcli connection show
NAME UUID TYPE DEVICE
Wired connection 1 a1b2c3d4-... ethernet enp0s1
my-static d4e5f6a7-... ethernet --
$ nmcli connection show "Wired connection 1" # detail of one connection
connection.id: Wired connection 1
connection.uuid: a1b2c3d4-...
connection.type: ethernet
ipv4.method: auto
ipv4.dns: --
...A connection with an empty DEVICE column (my-static) is a profile that’s only created and not activated.
Static IP configuration — the most frequently encountered task #
When you want to use a fixed IP rather than DHCP.
Creating a new connection #
$ sudo nmcli connection add \
type ethernet \
con-name "static-enp0s1" \
ifname enp0s1 \
ipv4.method manual \
ipv4.addresses 192.168.64.50/24 \
ipv4.gateway 192.168.64.1 \
ipv4.dns "192.168.64.1 8.8.8.8" \
autoconnect yesOptions explained:
con-name— human-recognizable connection nameifname— device name to applyipv4.method manual— fixed IP (DHCP isauto)ipv4.addresses— IP/prefix formipv4.gateway— default gatewayipv4.dns— DNS servers (multiple separated by space)autoconnect yes— auto-activate at boot
Modifying an existing connection #
$ sudo nmcli connection modify "static-enp0s1" \
ipv4.addresses 192.168.64.51/24
$ sudo nmcli connection up "static-enp0s1" # must reactivate to reflectmodify alone doesn’t reflect immediately. Reactivate with up or reconnect the device.
Switching connections #
$ sudo nmcli connection up "static-enp0s1"
$ sudo nmcli connection down "Wired connection 1"Even with two connections on the same device, only one is activated at a time. Useful for environments that move frequently (laptop’s office/home/cafe profiles).
Deletion #
$ sudo nmcli connection delete "static-enp0s1"nmtui — TUI tool #
Before you are fully comfortable with nmcli, nmtui (NetworkManager Text User Interface) is a quick way to get things done.
$ sudo nmtuiMove menus with arrow keys, enter static IP / DNS / gateway in forms. It is handy when you SSH into a machine in production and need to make a quick change without memorizing nmcli flags.
/etc/resolv.conf — DNS is managed by NetworkManager
#
In the old days you edited /etc/resolv.conf directly, but now NetworkManager manages it automatically. If you edit it directly, it gets overwritten on the next connection change.
DNS configuration is a property of the connection:
$ sudo nmcli connection modify "static-enp0s1" \
ipv4.dns "1.1.1.1 8.8.8.8" \
ipv4.dns-search "example.com"
$ sudo nmcli connection up "static-enp0s1"To ignore the DNS given by DHCP and use your own DNS:
$ sudo nmcli connection modify "Wired connection 1" \
ipv4.ignore-auto-dns yes \
ipv4.dns "1.1.1.1 8.8.8.8"Bonding — two NICs as one block #
When you have two physical NICs, bundling them for fault tolerance or bandwidth aggregation is bonding. Fault tolerance is by far the most common use case.
eth0 ─┐
├── bond0 ── 192.168.64.50
eth1 ─┘
(if one NIC dies the other takes over)Modes #
| mode | name | use |
|---|---|---|
0 | balance-rr | round robin (switch support required) |
1 | active-backup | one NIC active, others standby — most common |
2 | balance-xor | XOR hash distribution |
3 | broadcast | same packet on all NICs |
4 | 802.3ad / LACP | standard link aggregation — switch support required |
5 | balance-tlb | send-only distribution |
6 | balance-alb | both send and receive distribution |
99% of production setups use mode 1 (active-backup) or mode 4 (LACP). Mode 1 for simple fault tolerance, mode 4 when bandwidth aggregation is needed and the switch supports LACP.
Creating a bond #
# 1. create bond interface (master)
$ sudo nmcli connection add \
type bond \
con-name bond0 \
ifname bond0 \
mode active-backup \
miimon 100 \
ipv4.method manual \
ipv4.addresses 192.168.64.50/24 \
ipv4.gateway 192.168.64.1 \
ipv4.dns 192.168.64.1
# 2. attach two slaves to bond
$ sudo nmcli connection add \
type ethernet \
con-name bond0-slave1 \
ifname enp0s1 \
master bond0
$ sudo nmcli connection add \
type ethernet \
con-name bond0-slave2 \
ifname enp0s2 \
master bond0
# 3. activate
$ sudo nmcli connection up bond0-slave1
$ sudo nmcli connection up bond0-slave2
$ sudo nmcli connection up bond0miimon 100 is the link monitoring period (ms). It checks NIC state every 100ms and automatically removes a disconnected NIC.
Checking bond state #
$ cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v...
Bonding Mode: fault-tolerance (active-backup)
Currently Active Slave: enp0s1
MII Status: up
MII Polling Interval (ms): 100
Slave Interface: enp0s1
MII Status: up
Speed: 1000 Mbps
Duplex: full
Slave Interface: enp0s2
MII Status: up
Speed: 1000 Mbps
Duplex: fullCurrently Active Slave shows which NIC is active. Unplug the active NIC and the bond immediately fails over to the other — that is bonding’s core value.
Teaming — why was it deprecated #
In the RHEL 7–8 era, there was a similar tool called teaming alongside bonding. Its appeal was that it ran in user space, allowing more flexible policy configuration. But it was deprecated in RHEL 9 and removed in RHEL 10.
The reason is straightforward. Kernel-level bonding is more stable and faster, and its integration with NetworkManager and nmcli is smoother. Teaming’s flexibility never justified the added complexity. If you come across teamd or nmcli connection add type team in older posts, it is time to switch to bonding.
# old (deprecated): nmcli connection add type team ...
# now: nmcli connection add type bond ...Bridge — the foundation of virtual machines and containers #
A bridge is used when you want VMs or containers on the same network segment as the host. It ties the host’s physical NIC and virtual NICs together into the same L2 segment.
┌──── enp0s1 (external)
│
┌──── br0 ─┤
│ ├──── vnet0 (VM 1)
│ │
│ └──── vnet1 (VM 2)
│
192.168.64.50/24 (give the bridge an IP)Creating a bridge #
# 1. bridge interface
$ sudo nmcli connection add \
type bridge \
con-name br0 \
ifname br0 \
ipv4.method manual \
ipv4.addresses 192.168.64.50/24 \
ipv4.gateway 192.168.64.1 \
ipv4.dns 192.168.64.1
# 2. attach physical NIC to bridge
$ sudo nmcli connection add \
type ethernet \
con-name br0-slave1 \
ifname enp0s1 \
master br0
# 3. activate
$ sudo nmcli connection up br0With this, enp0s1 loses its IP, and br0 takes over that IP. When creating a VM, connect the network to br0 and it can communicate directly with other machines on the same LAN.
Trap — if you put the NIC the host is connected to via SSH directly into the bridge, the connection drops temporarily. Work in a state where console access is available, or apply at once via an automation script.
VLAN #
You can create virtual interfaces by attaching multiple VLAN tags to one NIC.
$ sudo nmcli connection add \
type vlan \
con-name vlan100 \
ifname enp0s1.100 \
dev enp0s1 \
id 100 \
ipv4.method manual \
ipv4.addresses 10.0.100.50/24id 100 is the 802.1Q VLAN tag. The resulting interface name is usually in the form <base>.<id> (enp0s1.100).
Diagnostic tools #
ip command
#
ip is the most direct command. It reads the kernel’s network state directly, bypassing NetworkManager.
$ ip -4 addr show # IPv4 addresses
$ ip -6 addr show # IPv6
$ ip route # routing table
$ ip neigh # ARP table
$ ip link show # link state (up/down)Connectivity #
$ ping -c 3 8.8.8.8
$ traceroute google.com
$ mtr -n google.com # ping + traceroute integrated (interactive)Ports / sockets #
$ ss -tlnp # TCP listening + process
$ ss -tunap # TCP+UDP all sockets + process
$ ss -s # socket stats summarynetstat is deprecated. If you are just starting out, use ss exclusively.
DNS #
$ dig example.com # DNS response detail
$ dig +short example.com # IP only
$ host example.com # short response
$ nslookup example.com # old tool, for compatibilityIn operations, dig is the standard. +short is frequently used.
AlmaLinux / Rocky differences #
All commands in this post work as is. NetworkManager / nmcli / bonding / bridge / VLAN all bring the RHEL packages as is.
Frequently encountered traps #
“Grabbed a static IP but can’t communicate externally” #
99% of the time it’s because the gateway or DNS was missed. Check ipv4.gateway and ipv4.dns with nmcli connection show <name>.
“Changed DNS but /etc/resolv.conf stays the same”
#
The file was edited directly instead of updating the connection property. NetworkManager overwrites it on the next change. Always use nmcli connection modify ... ipv4.dns ... instead.
“Modified but no change” #
An activated connection requires reapply with up after modify to be reflected. Or reconnect the device.
“bond / bridge doesn’t come up after boot” #
Check that the slave connections have connection.autoconnect set to yes. NetworkManager brings up the master first and then attaches the slaves, but slaves that are not set to autoconnect will never attach.
“Two connections collide” #
If two connections exist on the same device, which one activates at boot is unpredictable. Delete unused connections immediately or set connection.autoconnect no on the ones you want dormant.
Frequently used commands at a glance #
| Command | What it does |
|---|---|
nmcli | overall state at a glance |
nmcli device status | device list |
nmcli device show <dev> | device detail |
nmcli connection show | connection list |
nmcli connection show <name> | connection detail |
nmcli connection add type ethernet ... | add new connection |
nmcli connection modify <name> <key> <value> | modify property |
nmcli connection up/down <name> | activate / deactivate |
nmcli connection delete <name> | delete |
nmcli connection reload | re-read disk changes into NM |
nmtui | TUI |
ip -4 addr show / ip route / ip link | kernel state |
ss -tlnp | listening ports + process |
dig +short <host> | DNS lookup |
Wrapping up #
The flows organized in this post:
- The network standard for RHEL 9 is the single tool NetworkManager. ifcfg / network service is gone.
- The core model is Device (NIC) + Connection (config profile). Multiple connections per device are possible.
nmcliis the daily command. The first line is alwaysnmclito see at a glance.- Static IP pattern:
nmcli connection add type ethernet ... ipv4.method manual ipv4.addresses ... gateway ... dns .... - DNS is a property of the connection — direct editing of
/etc/resolv.confis forbidden. - 99% of bonding is mode 1 (active-backup), or mode 4 in environments where LACP is possible. Teaming is deprecated.
- Bridge is the foundation for placing VMs / containers on the same LAN as the host.
- For diagnostics,
ip/ss/digare the standard (netstat is deprecated).
Next — log management #
Once the network is configured, everything that happens on top of it eventually ends up in logs. The next post looks at how to handle them.
In #5 Log management — journald, rsyslog, log rotation we’ll organize from an operational perspective: journald’s retention policy and disk usage control, rsyslog that’s the old standard but still alive and remote log collection, and logrotate that automatically rotates all log files by certain sizes / periods.