NetworkManager Crash Course: nmcli and Keyfiles for RHEL Servers
NetworkManager is the daemon that configures the network on every RHEL-family server, and since RHEL dropped the old network-scripts it is the only supported way to do it. This crash course runs it the way a dedicated server uses it: headless, static, over SSH, with nmcli and keyfiles instead of a desktop applet. You will learn the device-and-profile model that clears up most nmcli confusion, a full static IPv4 and IPv6 setup you can paste and adapt, where profiles live on disk across RHEL 8, 9, and 10, and how to change the network on a remote box without locking yourself out.
Loading...
What NetworkManager is, and whether you're running it
NetworkManager is the daemon that decides what your network interfaces do: which addresses they carry, which gateway they route through, which DNS servers they resolve against. On the RHEL family (RHEL, and the community rebuilds AlmaLinux and Rocky) it is the default, and since the network-scripts package was dropped it is the only network stack Red Hat supports. If you are on RHEL 8, 9, or 10 or one of their rebuilds, NetworkManager is already running and already holds your configuration, whether or not you have touched it.
This crash course is written for one setup: a dedicated server with a static public IPv4 address, perhaps a routed IPv6 block, no Wi-Fi, reached only over SSH. That is a different job from the laptop case most NetworkManager tutorials assume, so we lead with static addressing and the command-line tool, nmcli, and treat the graphical pieces as a footnote. By the end you will have a static IPv4 and IPv6 profile you can paste and adapt, a clear picture of where the configuration lives, and enough of the model to debug it when a change does not take. The nmcli commands here are stable across releases, so the exact version rarely matters for what follows; at the time of writing the current stable series is NetworkManager 1.56 (February 2026). Check the release notes for what your distribution ships.
One theme runs through all of it: on a remote box, a networking mistake locks you out. NetworkManager's design gives you a safety margin here, and out-of-band console access (IPMI, iKVM, or a provider serial console) is the backstop for when the margin is not enough. Both come up where they matter below.
Assumptions for the examples:
- A single Ethernet interface,
enp1s0. Yours may beeno1,ens3, or similar;nmcli device statusprints the real name. - Root access over SSH, or a user with
sudo. - Addresses drawn from the documentation ranges in RFC 5737 and RFC 3849, so nothing here routes. Substitute your own values throughout.
Devices, profiles, and the two states
Most nmcli confusion comes from missing two distinctions. Learn these and the commands stop feeling arbitrary.
The first is device versus connection profile. A device is the hardware: enp1s0, the physical NIC. A connection profile is a saved set of settings that can be applied to a device: an address, a gateway, DNS, and a long list of other properties. The relationship is one-to-many. A single device can have several profiles saved against it (one static, one DHCP, one for a maintenance VLAN), and only one is active at a time. When you run nmcli con up, you are choosing which saved profile the device should wear right now. So "configuring the network" in NetworkManager is two steps: edit a profile, then activate it.
The second is saved state versus running state. Editing a profile with nmcli con mod writes to disk. It does not touch the live interface. The interface keeps whatever it got when its current profile was last activated, until you activate again. That gap is not a quirk to work around; it is the safety feature. You can rewrite the address, gateway, or a route on the profile carrying your SSH session, read back exactly what you changed, and only then commit by reactivating. Nothing you typed reaches the wire until that moment.
Hold both ideas together and the workflow reads plainly. Pick or create a profile. Modify its properties, which is saved but inert. Activate it, which is live. Everything past this point is which properties and which flags.
Where the configuration lives, and the ifcfg history
Saved profiles are keyfiles: small INI-style files under /etc/NetworkManager/system-connections/, one per profile, each named <profile>.nmconnection. They can hold secrets such as a Wi-Fi PSK or 802.1X credentials, so NetworkManager refuses to read any file that is not owned by root and locked to permission 0600. A profile you create with nmcli gets those permissions for free; a file you drop in by hand needs chown root:root and chmod 600, or the daemon ignores it.
Run RHEL for a few years and you remember a different location: /etc/sysconfig/network-scripts/ifcfg-*. The move off those files happened in stages, and which stage your server sits on changes what you will find.
- RHEL 8 deprecated the legacy
network-scriptsservice. NetworkManager became the default andifup/ifdownwere rewired to call it, but the on-disk default was still the old format: new profiles were written asifcfg-*files. - RHEL 9 flipped that default. New profiles are written as keyfiles under
system-connections/. Existingifcfg-*files are still read through a compatibility plugin, so an upgraded machine keeps working, but the format was formally deprecated andnmcli connection migratewas added to convert profiles across. - RHEL 10, released May 2025, finished the job. The ifcfg plugin is gone, NetworkManager ignores anything in
/etc/sysconfig/network-scripts/, and there is no in-place conversion left on the box. Keyfiles are the only format it reads.
The practical upshot: on anything from RHEL 9 onward, treat system-connections/*.nmconnection as the source of truth. One catch when you edit those files directly is that NetworkManager does not watch the directory. After a hand edit, run nmcli con reload to re-read every profile, or nmcli con load <path> for a single file, then activate. Skip the reload and the daemon is still working from the copy it read last.
A complete static configuration
Here is a full static profile for enp1s0: an IPv4 address and gateway, two DNS servers, a static IPv6 address and gateway, and autoconnect on so it returns after a reboot. Change the values and run it as one command:
nmcli con add type ethernet con-name static-enp1s0 ifname enp1s0 \
ipv4.method manual \
ipv4.addresses 203.0.113.10/24 \
ipv4.gateway 203.0.113.1 \
ipv4.dns "1.1.1.1,9.9.9.9" \
ipv6.method manual \
ipv6.addresses 2001:db8:2:a::10/64 \
ipv6.gateway 2001:db8:2:a::1 \
ipv6.dns "2606:4700:4700::1111" \
connection.autoconnect yes
ipv4.method manual is what makes it static; the DHCP equivalent is auto. Addresses take CIDR notation. ipv4.dns takes a comma-separated list. The IPv6 half mirrors the IPv4 half property for property, so a dual-stack server is one command rather than two profiles. connection.autoconnect yes is already the default, but stating it is cheap insurance against a profile that works until the first reboot and then does not.
con add creates and saves the profile. On most provisioned servers the interface already has an active profile (often a DHCP one from install), so nothing on the wire changes yet. Read it back before you commit:
root@server01:~# nmcli -f ipv4.addresses,ipv4.gateway,ipv6.addresses con show static-enp1s0
ipv4.addresses: 203.0.113.10/24
ipv4.gateway: 203.0.113.1
ipv6.addresses: 2001:db8:2:a::10/64
When it reads back the way you meant, activate it:
root@server01:~# nmcli con up static-enp1s0
This is the dangerous line, and the one place to slow down. Activating this profile deactivates whatever was carrying your session. If the gateway is wrong or the address collides, SSH drops and does not come back, because the thing you would fix it over is the thing you just broke. Two habits keep this safe: get the values right by reading them back first, and have the server's IPMI or serial console open in another window before you press enter. NetworkManager will not roll a raw nmcli con up back for you; the console is your undo.
To change one property later, modify and reactivate:
root@server01:~# nmcli con mod static-enp1s0 ipv4.dns "9.9.9.9"
root@server01:~# nmcli con up static-enp1s0
The con mod line is saved but inert. The con up line is what puts it on the wire.
Reading what NetworkManager knows
Four commands answer almost every "what is going on" question.
nmcli device status is the first thing to run on any server. One line per interface: its type, whether NetworkManager manages it, and which profile is active.
root@server01:~# nmcli device status
DEVICE TYPE STATE CONNECTION
enp1s0 ethernet connected static-enp1s0
lo loopback connected (externally) --
nmcli con show lists saved profiles, active or not. Add a name to dump every property of one profile, which is how you see what a profile will do before you activate it:
root@server01:~# nmcli con show
NAME UUID TYPE DEVICE
static-enp1s0 6d2c8f5a-1e3b-4a7d-9c11-2f0a7b3e5d84 ethernet enp1s0
The -f flag selects fields so you are not scrolling two hundred lines, and -g (get) prints a single value with no header, which is what you want in a script:
root@server01:~# nmcli -g ipv4.addresses con show static-enp1s0
203.0.113.10/24
The output above is illustrative; UUIDs and exact column spacing vary by version and host.
nmtui for a form instead of flags
If you would rather fill in a form than remember property names, nmtui is the curses front end to the same engine. Run it over SSH or on the console and you get three choices: edit a connection, activate a connection, and set the system hostname. The connection editor exposes IPv4 and IPv6 addressing, DNS, and the fields for bonds, bridges, and VLANs, so a static setup is doable without touching nmcli at all. Jump straight in with nmtui edit enp1s0 or nmtui connect.
Its limits are worth knowing. nmtui cannot be scripted, and it does not expose every property: routing rules, dispatcher scripts, and the more obscure settings still need nmcli or a keyfile edit. As a guided way back when you have lost the network and are sitting at a console, it earns its place.
nmcli cheat sheet
The commands you will reach for most, with static-enp1s0 standing in for your profile name:
| Task | Command |
|---|---|
| List interfaces and their state | nmcli device status |
| List saved profiles | nmcli con show |
| Show one profile in full | nmcli con show static-enp1s0 |
| Create a static profile | nmcli con add type ethernet con-name static-enp1s0 ifname enp1s0 ipv4.method manual ... |
| Change a property | nmcli con mod static-enp1s0 ipv4.dns 9.9.9.9 |
| Apply a change (reactivate) | nmcli con up static-enp1s0 |
| Deactivate a profile | nmcli con down static-enp1s0 |
| Re-read keyfiles after a hand edit | nmcli con reload |
| Switch a profile to DHCP | nmcli con mod static-enp1s0 ipv4.method auto |
| Set the system hostname | nmcli general hostname server01 |
| Stop NM managing an interface | nmcli device set enp1s0 managed no |
| Watch the log live | journalctl -u NetworkManager -f |
Who defaults to NetworkManager
Which stack you get is mostly a consequence of the distribution you installed, a separate decision from this one (covered in our guide to choosing a Linux distro). The split as it stands in 2026:
| System | Default network stack | NetworkManager here? |
|---|---|---|
| RHEL, AlmaLinux, Rocky (8, 9, 10) | NetworkManager | Yes, and the only supported option |
| Fedora | NetworkManager, keyfile format | Yes |
| Debian 13 with a desktop | NetworkManager | Yes |
| Debian 13 server or minimal | ifupdown (/etc/network/interfaces) | No by default |
| Ubuntu Server | Netplan to systemd-networkd | No |
| Ubuntu Desktop | Netplan to NetworkManager | Yes, through Netplan |
The Debian server row catches people out. Install network-manager onto a Debian server that already uses ifupdown and it does not take over the existing interfaces; anything declared in /etc/network/interfaces stays with its old renderer and shows as unmanaged in nmcli. That is intentional, and it is the safe default.
VLANs, bonds, and bridges
Three server patterns go past a single flat address. Each is one profile type, and the syntax rhymes with the static example.
A VLAN puts tagged traffic on a virtual interface layered over a physical one. This adds VLAN 40 on enp1s0 with its own address:
nmcli con add type vlan con-name vlan40 ifname enp1s0.40 dev enp1s0 id 40 \
ipv4.method manual ipv4.addresses 198.51.100.10/24
The parent enp1s0 keeps carrying untagged traffic. The switch port has to be a trunk that passes VLAN 40, or the tagged frames go nowhere.
A bond aggregates two NICs into one logical link. LACP (mode 802.3ad) is the common server choice for throughput and link failover:
nmcli con add type bond con-name bond0 ifname bond0 \
bond.options "mode=802.3ad,miimon=100,lacp_rate=fast,xmit_hash_policy=layer3+4"
nmcli con add type ethernet con-name bond0-p1 ifname eno1 master bond0 slave-type bond
nmcli con add type ethernet con-name bond0-p2 ifname eno2 master bond0 slave-type bond
LACP is negotiated, not one-sided. The switch needs a matching link-aggregation group on those two ports, or the bond stays down. Put the addressing on bond0, never on the member ports.
A bridge turns the host into a virtual switch, which is what a KVM hypervisor needs so its guests reach the network directly. The host IP moves onto the bridge, and the physical NIC becomes a port with no address of its own:
nmcli con add type bridge con-name br0 ifname br0 \
ipv4.method manual ipv4.addresses 203.0.113.10/24 ipv4.gateway 203.0.113.1 ipv4.dns 1.1.1.1
nmcli con add type ethernet con-name br0-port ifname enp1s0 master br0 slave-type bridge
Moving the host address onto a bridge over SSH is the same lockout risk as any reactivation, sharper because you are restructuring the interface the session rides on. Build it from the console, or on a second NIC you are not logged in through. This is the same bridge that Proxmox sets up as vmbr0; here you build it by hand.
Troubleshooting
- A device shows
unmanaged. NetworkManager has been told to ignore it. Look in/etc/NetworkManager/NetworkManager.confand/etc/NetworkManager/conf.d/*.conffor amanaged=falseline or a[device-*]section withmanaged=0. On Debian, interfaces defined in/etc/network/interfacesshow as unmanaged by design. To hand a device back for the session,nmcli device set enp1s0 managed yes; to make it stick, remove the config that excluded it. - A change did not apply. Almost always one of two things: you ran
con modbut nevercon up, so the edit is saved and inert, or you hand-edited a keyfile and never rannmcli con reload, so the daemon is still on the old copy. Neither fails loudly. Both clear the moment you activate. - Saved and live disagree.
ip addrandip routeshow what the kernel is doing this second;nmcli -f all con show <name>shows what the profile will do next time it comes up. When the address on the wire does not match the profile, an activation is pending, not a bug. - Nothing above explains it. Read the daemon's own log:
journalctl -u NetworkManager -bfor this boot, or-fto watch it live in one window while you runcon upin another. NetworkManager is talkative about why an activation failed, and the reason is usually in the last handful of lines.
nmstate, the declarative layer
For fleets, and for anyone who prefers describing the end state over issuing steps, Red Hat ships nmstate and its command nmstatectl. You write the network you want as YAML, run nmstatectl apply, and nmstate drives NetworkManager (its only backend) to match. What makes it worth reaching for on a remote server is the rollback: apply a state that breaks connectivity and nmstate reverts to the previous one on its own, which raw nmcli con up does not do. It is available on RHEL 8, 9, and 10, and it is what the RHEL system role for networking uses underneath. For a single box, nmcli is enough. When the same config has to land on fifty servers identically and safely, nmstate is the layer built for that.
Ubuntu Server uses Netplan, not NetworkManager
If you also run Ubuntu servers, none of the nmcli above applies there by default. Ubuntu Server ships Netplan writing to systemd-networkd, configured in /etc/netplan/*.yaml, with no NetworkManager in the picture. Ubuntu Desktop uses Netplan too, but with NetworkManager as the renderer, so nmcli works there. The concepts carry over (declared config, an activation step, a saved-versus-live split), but the tool and the files differ. The Netplan crash course is the sibling to this guide for that side of the estate.
Deploying on Serverside
A NetworkManager profile is only as useful as the network it points at. Serverside runs its own network (AS55285), so a RHEL, AlmaLinux, or Rocky server provisions in under a minute, and the public IPv4 and IPv6 you configure sit behind always-on DDoS mitigation from the first boot. The RHEL dedicated line ships the RHEL family ready for the nmcli workflow above, and the wider dedicated range covers the rest.
Related reading: the AlmaLinux versus RHEL subscription breakdown if you are weighing a licence against a free rebuild, and the Linux server hardening checklist to run once the interface is up and reachable.
FAQ
Why did my IP change not survive a reboot?
Three usual causes. The profile has autoconnect turned off, so nothing activates it at boot: set connection.autoconnect yes. You changed the profile with nmcli con mod but never ran nmcli con up, so the edit was saved and never applied. Or you hand-edited the keyfile and never ran nmcli con reload, so NetworkManager kept using the copy it read at start. Activating the profile after the change fixes all three.
Should I edit the .nmconnection files by hand or use nmcli?
Either works, and the keyfile is the source of truth in both cases. nmcli checks the values you give it and reloads the profile for you, so it is the safer default. If you edit the file directly, keep it owned by root and set to permission 600, then run nmcli con reload so the daemon picks up the change. NetworkManager does not watch the directory on its own.
How do I stop NetworkManager from managing an interface?
For the current session, run nmcli device set enp1s0 managed no. To make it persist across reboots, add a [device-*] section with managed=0 to a file under /etc/NetworkManager/conf.d/. This is common when another tool owns a particular NIC, or on Debian where interfaces defined in /etc/network/interfaces are left unmanaged on purpose.
Do I still have ifcfg files on RHEL?
It depends on the version. RHEL 8 still wrote ifcfg files by default. RHEL 9 reads existing ones but writes new profiles as keyfiles and marks the ifcfg format deprecated. RHEL 10, from May 2025, removed the ifcfg plugin entirely and ignores those files. On RHEL 9 and 10 the keyfiles under /etc/NetworkManager/system-connections are what count.
NetworkManager or systemd-networkd for a server?
On the RHEL family the decision is made for you: NetworkManager is the default and the only supported stack, and it handles static server addressing without trouble. Ubuntu Server goes the other way, using systemd-networkd through Netplan. Both are production-grade. Use whichever your distribution ships rather than replacing it.

Written by
Co-founder & CTO, Serverside.com
Jesse is the co-founder and CTO of Serverside.com, where he leads the engineering behind the company's bare-metal cloud: from the ASN 55285 backbone to the core automation that drives day-to-day operation. He writes about dedicated servers, operating systems, and running production workloads on bare metal.
Keep reading
View all articlesEnjoyed this article?
Get new guides and engineering write-ups in your inbox. No spam, unsubscribe anytime.




Canadian and
Dutch