A nightly backup on an Ubuntu server fails after a reboot with Could not resolve host: backup.example.net. The unit has After=network-online.target, and systemctl status network-online.target says active. You are logged in over the network it says it cannot reach. Every page that message leads to says “DNS”, and DNS is fine. Nothing on the machine configured the interface at all. The program that should have was looking for a name the interface no longer has, and the target that promised a network was reached because there was nothing left to wait for. An interface has no owner and no state of its own. The kernel holds the state, six programs take turns writing it, and each one’s status command shows the kernel’s current value under its own banner.

This page follows one interface through eight stages, from the kernel creating it to the records each program left behind. It sits underneath ip, which owns the commands, and Networking, Explained, which owns what an address and a route are. It stops where The Life of a Packet begins, at the route lookup, and hands the resolver to DNS, All the Way Down. Everything was measured on Ubuntu 24.04 (systemd 255, netplan 1.1.2, NetworkManager 1.46, dhcpcd 10.0) with real DHCP servers on the wire. Where a claim comes from source rather than a measurement, the page says so; IPv6 in particular is read from the source, not measured.

Eight stages, and no two of them are done by the same program. The kernel creates and names the interface, and udev renames it. A manager, or two, claims it. A DHCP server on another machine chooses its address, keyed on an identifier the client chose. The client writes routes and hands DNS to the resolver. A wait-online program decides what “online” means. Every later event rewrites some of it, and each party keeps its own record of what it did.

  1. The kernel names it — an ifindex that never changes, and a name that is about to.
  2. udev renames it — a versioned computation over sysfs, a policy list, and a rename cloud-init repeats every boot.
  3. A manager claims it, or two do — netplan is a generator, not a daemon; the claims are independent and can both be true.
  4. An identity, then an address — four clients on one Ubuntu machine present four different identities, and the server keys the lease on it.
  5. Routes and the resolver — metrics that differ by writer, and the option that silently removes your default gateway.
  6. “Online” — three definitions, none of them “has an address”.
  7. The change — renewals, carrier, reconfigure: event-driven writers that trust the kernel between events.
  8. The record — eight places, none of them the kernel, and the one that disagrees with it names the party.

The hinge is three lines, and it does not depend on knowing what manages the machine. The first question is what the kernel says the interface is. The second is who claims it: both managers’ own lists, side by side. The third is how it got that name.

ip -br link; ip -br -4 addr
networkctl list 2>/dev/null; nmcli -t -f DEVICE,STATE,CONNECTION device 2>/dev/null
udevadm info /sys/class/net/X | grep -E 'ID_NET_(NAME|LINK|NAMING)|NM_UNMANAGED'
What you seeStageWho decided
Your interface is not in ip -br link, but an enp… or ens… you did not expect is1udev, or cloud-init’s set-name. Compare INTERFACE with ID_NET_NAME_* and read ID_NET_NAMING_SCHEME
ID_NET_NAME_PATH is present, ID_NET_NAME_SLOT is not, and your config says ens…1the firmware or hypervisor stopped exposing a slot, or the scheme version moved
ID_NET_LINK_FILE is not 99-default.link1whoever wrote that .link: netplan (10-netplan-*) or you
NM_UNMANAGED=1 among the udev properties2udev told NetworkManager to leave it alone (85-nm-unmanaged.rules or netplan’s 90-netplan.rules)
SETUP unmanaged in networkctl and STATE unmanaged in nmcli, or nmcli absent2nobody. No .network matched and NetworkManager declined. The config names an interface that does not exist under that name: go back to stage 1
SETUP configured and STATE connected for the same link2both: two managers. Expect two addresses, two default routes, two leases
SETUP configuring, OPERATIONAL no-carrier2the wire. Wait-online will block on this link unless it is optional: true
SETUP configuring, OPERATIONAL carrier, and no IPv4 line in ip -br -4 addr3the DHCP server did not answer this client’s identifier. Its lease table, on another machine, is the record
169.254.x.x present3dhcpcd, after about ten seconds with no answer. networkd and NetworkManager never write this address
Two default via lines in ip route4two writers. Metric 100 is NetworkManager or netplan, 1024 bare networkd, 100N dhcpcd
No default line, but the lease carries a router4your DHCP server sent option 121 and the client obeyed RFC 3442 by dropping option 3
systemctl status systemd-networkd-wait-online: Condition check resulted in … being skipped5netplan decided no interface was worth waiting for, because none it knew about existed
… wait-online: Timeout occurred while waiting for network connectivity5a managed link never left configuring, or nothing at all is managed (bare networkd)
An address you did not add appears, or one you added disappears, with no log line6the other writer’s next event: a renewal, a carrier flap, netplan apply, nmcli connection up
networkctl status or nmcli device show shows an address whose lease file says something else7the kernel’s value printed under a manager’s banner. Read the lease file, not the status line

Two limits. The table names the party that wrote the value you can see, not why it chose it; the why is in that party’s own record, stage 7. And on a machine with neither networkctl nor nmcli, the middle line prints nothing, which is the answer: the owner is a script, and ps -C dhclient,dhcpcd,udhcpc -o args names it.

Stage 0 — The kernel names it

When a driver registers a network device the kernel gives it two identities. The first is an ifindex, a small integer that never changes for the life of the object and that every other party will use to refer to it. The second is a name from the driver’s template, eth%d for almost every ethernet driver, filled in by probe order: the first NIC the driver sees is eth0. The kernel records how the name was made in /sys/class/net/eth0/name_assign_type: 1 means enumerated, and that number is what the next stage reads before deciding whether to rename.

Probe order is the whole problem. Two NICs on one machine can swap eth0 and eth1 between boots if their drivers initialise in a different order, and nothing in the kernel promises otherwise. What the kernel does promise is where the device is: readlink -f /sys/class/net/eth0 prints its position on the bus, /sys/devices/pci0000:00/0000:00:08.0/virtio7/net/eth0 on the lab VM. Every stable name in the next stage is a function of that path.

Stage 1 — udev renames it

The rename is two steps by two udev builtins, and most explanations collapse them. net_id computes every name each scheme would give and exports them as properties. It does not pick one. udevadm test-builtin net_id /sys/class/net/eth0 shows the work, and it is safe on every version:

Using default interface naming scheme 'v255'.
eth0: MAC address identifier: hw_addr=02:fc:00:00:00:01  x02fc00000001
0000:00:08.0: Could not obtain onboard index: No such file or directory
eth0: PCI path identifier: domain=n/a bus_and_slot=p0s8 func=n/a port=n/a  enp0s8
ID_NET_NAMING_SCHEME=v255
ID_NET_NAME_MAC=enx02fc00000001
ID_NET_NAME_PATH=enp0s8

No ID_NET_NAME_ONBOARD and no ID_NET_NAME_SLOT: this hypervisor exposes neither an onboard index nor a slot number for that device, so the only stable candidate is the PCI path. A hypervisor that does expose a slot yields ens3 for the same VM image; move the image and the name moves with it. The pick is made by the second builtin, net_setup_link, applying the first .link file whose [Match] fits. The default is /usr/lib/systemd/network/99-default.link, with NamePolicy=keep kernel database onboard slot path: keep a name userspace already set, keep one the kernel marked predictable, then try each property in that order and take the first that exists. mac is not in the list, which is why enx… is a primary name only for USB NICs, from a separate .link. Since systemd 257 every NIC also carries it as an altname, visible in ip -d link.

The scheme is a version, not a property of the hardware. ID_NET_NAMING_SCHEME=v255 is Ubuntu 24.04; 26.04 ships v259. The table of what changed between them is in systemd.net-naming-scheme(7). v257 started using the firmware’s sun attribute for slot names. v256 added an allow-list, because a kernel upgrade that starts exposing phys_port_name appends a suffix to the name. Ubuntu does not pin the scheme. Red Hat defines one per release, and even rhel-9.3 and rhel-9.5 changed some names. So the real gate on an interface name is systemd version times kernel version times firmware. The header file that defines the schemes says so: a scheme makes names more stable across OS versions, “but not fully stabilize them”.

Three more parties rename after udev, and the page needs all three. A .link file of your own in /etc/systemd/network/ renames by MAC. But the rename happens on the add event, in the initramfs, from the copy the initramfs generator took when the image was built. Touch a .link and rebuild the initramfs, on initramfs-tools or dracut; the running system applies it only after udevadm trigger --action=add. Netplan, for a stanza with match: macaddress and set-name, writes both a .link and a udev rule that says NAME=. And cloud-init renames by MAC in its local stage on every boot, even when it does not re-render its YAML. The code path is annotated “nothing new, but ensure proper names”, and it will take a live interface down to do it. That is why the name in your config is somebody else’s decision, three times over.

The same default .link does one more thing nobody expects. MACAddressPolicy=persistent gives virtual devices a MAC derived from the name and the machine-id. Measured: udev processing changed a veth’s address from the random one the kernel assigned to a new one, and addr_assign_type went from 1 to 3. A DHCP reservation, an ATTR{address}== rule or a client identifier built from the pre-udev MAC now points at an address that no longer exists. One warning that belongs here rather than in a footnote: on systemd 255 and earlier, udevadm test is not a dry run. It renamed the lab VM’s live interface from eth0 to enp0s8. Version 256 made it read-only; test-builtin was always safe.

Stage 2 — A manager claims it, or two do

On Ubuntu the file you edit is netplan’s, and netplan is not a daemon. It is a generator: netplan generate reads the YAML and writes files for whichever renderer the YAML names, under /run. For a cloud-init-shaped stanza under the default renderer it writes a .link, a .network for systemd-networkd, a udev rule that sets NM_UNMANAGED=1 so NetworkManager keeps off, and a drop-in for the wait-online service (stage 5). Read what it rendered, because it contains decisions you did not write:

# /run/systemd/network/10-netplan-eth0.network
[Match]
PermanentMACAddress=02:fc:00:00:00:01
Name=eth0
[Network]
DHCP=ipv4
LinkLocalAddressing=ipv6
[DHCP]
RouteMetric=100
UseMTU=true

RouteMetric=100 and UseMTU=true are netplan’s, not networkd’s: bare networkd uses a metric of 1024 and ignores the DHCP MTU option. Ubuntu Desktop’s shipped YAML is one line, renderer: NetworkManager, and the only thing netplan renders from it is an empty file at /run/NetworkManager/conf.d/10-globally-managed-devices.conf. That masks the shipped file of the same name, whose one line is unmanaged-devices=*,except:type:wifi,…. “Ubuntu Desktop uses NetworkManager” means, literally, that netplan un-forbids it.

The two claims are independent. networkd claims a link when a .network file’s [Match] fits it; NetworkManager claims everything not marked NM_UNMANAGED and not listed in unmanaged-devices. Both daemons can be enabled and active on one machine with no conflict, which is why systemctl is-active NetworkManager systemd-networkd answers a different question from the one being asked. Ownership is per link: networkctl list prints a SETUP column, configured, configuring or unmanaged, and nmcli device status prints STATE, connected, unmanaged or connected (externally). Measured side by side, the same veth was connected to NetworkManager and unmanaged to networkd until a .network was written for it, and then it was both.

Carrier lives inside the claim. A claim means “I will configure this when it has a wire”, and a claimed link without one shows as configuring in networkd and unavailable in NetworkManager. Read it from the flags, not the state word. In ip -br link, UP inside the angle brackets is administrative and NO-CARRIER is the wire. The state column prints DOWN for a physical NIC with no cable whether or not it is administratively up. Only stacked devices, veth and VLANs, print LOWERLAYERDOWN there.

Stage 3 — An identity, then an address

The DHCP server does not give an interface an address. It gives a client identifier an address, and the client chooses the identifier. Option 61 on the wire is the client’s statement of who it is, and one Ubuntu machine can make four different statements depending on which client ran. Measured with tcpdump on the server side, same MAC each time:

ClientOption 61 on the wireWhere the identity comes from
isc dhclient (the Debian/Ubuntu config)nonesend dhcp-client-identifier is commented out in /etc/dhcp/dhclient.conf; the server falls back to the MAC
NetworkManager 1.16 and later01: + MACNetworkManager changed its default from a DUID to the MAC in 1.16
busybox udhcpc01: + MACthe MAC
systemd-networkdff: + IAID + DUID-EN, a hash of /etc/machine-idClientIdentifier=duid is the default; the IAID is derived from the MAC
dhcpcd 10 (shipped by Ubuntu in place of dhclient since 24.04)ff: + IAID + DUID-LLT/var/lib/dhcpcd/duid, built from the first MAC it saw and a timestamp

Then the server decides what a changed identity means, and the two servers measured agree on the rule: the address changes when the live lease carries a different, non-empty client identifier. Against dnsmasq, dhclient got .167, udhcpc got the same .167, dhcpcd got .119. Against Kea, dhclient got .100, dhcpcd got the same .100 because the existing lease had no identifier and matched by hardware address, and udhcpc got .101. So the advice that “moving to systemd-networkd changes your address” is wrong in the case people mean, dhclient to networkd, and right in the one they do not. NetworkManager to networkd, or back, changes the address while the old lease lives, because 01:MAC is not ff:IAID:DUID. Ubuntu’s dhcpcd is a third identity, different from both. Netplan leaves networkd’s default alone unless the YAML says dhcp-identifier: mac.

What happens when no server answers is the sentence this page corrects in Networking, Explained. A 169.254.x.x address does not mean “DHCP failed” on the machines this site’s readers run. networkd’s LinkLocalAddressing= defaults to ipv6, so it writes no IPv4 address at all, and NetworkManager’s ipv4.link-local resolves to disabled when the method is auto. Measured: NetworkManager on a link with no server logged ip-config -> failed (reason 'ip-config-unavailable') after 45 seconds and left the interface UP with no IPv4 line, and networkd stayed at configuring, also with nothing. Only dhcpcd does what the folklore says, at 11 seconds: using IPv4LL address 169.254.181.155, plus a default route via that interface at metric 1001016. On two of the three clients the dangerous case is silent, and the hinge’s first line is where you see it: UP, carrier, no address.

IPv6, from the source rather than the lab: networkd accepts router advertisements by default unless forwarding or masquerading is on for the link, which a Docker host often has. It writes its IPv6PrivacyExtensions= default of no into the kernel’s use_tempaddr for every link it manages. NetworkManager has no default of its own and reads the kernel’s, which on Ubuntu the procps package sets to 2. So on one Ubuntu machine, SLAAC temporary addresses are on under NetworkManager and forced off under networkd, from the same sysctl file.

Stage 4 — Routes and the resolver

The default route’s metric tells you who wrote it. NetworkManager uses 100 for ethernet and 600 for wifi, and reserves a distinct metric per device, so a second ethernet device gets 101 or 102. Bare networkd uses 1024; netplan overrides it to 100 for compatibility. dhcpcd uses 1000 plus the ifindex. Two default routes at different metrics coexist without complaint, ip route get picks the lower, and the other is silently unused. Adding a second at the same metric fails with RTNETLINK answers: File exists, which is the only time the kernel says anything about it.

The route fact almost nobody has is about option 121. A server that adds classless static routes to hand out one extra route also removes the default gateway from most clients, because RFC 3442 says a client that receives option 121 must ignore option 3. Measured: dhclient’s Debian script, dhcpcd and networkd all dropped the default route the moment 121 appeared in the lease; NetworkManager 1.46 kept it, and NetworkManager 1.58, in Ubuntu 26.10, drops it too. Include 0.0.0.0/0 in the option or lose the default route across the fleet at the next renewal. The MTU option is the same story in miniature. dhclient, udhcpc and NetworkManager set the link MTU. dhcpcd applies it to its routes and leaves the link at 1500. networkd ignores it unless UseMTU=true, which netplan sets and a hand-written .network does not.

DNS servers reach systemd-resolved by two different roads, and both end in the per-link table that resolvectl status prints. networkd and NetworkManager push them over D-Bus. The script-driven clients call resolvectl dns IFACE … from a hook, and if the hook is missing the link section in resolvectl status is empty while the lease has servers. One Ubuntu-specific gate the DNS pages do not know: upstream UseDomains= defaults to no, but Ubuntu patched it to yes in 2017 and dropped the patch in 24.10. On 24.04 Server a search list handed out by a café’s DHCP is applied; on 26.04 it is not unless the YAML says use-domains: true. DNS, All the Way Down owns what happens next.

Stage 5 — “Online”

systemd’s own document says it plainly. network.target “indicates that the network management stack has been started”, and whether any interface is configured when it is reached “is not defined”. network-online.target waits until the network is up, “where the definition of ‘up’ is defined by the network management software”. So there are three definitions, and none of them is “has an address”.

Bare networkd: systemd-networkd-wait-online waits until every managed link is configured or failed, and at least one has reached operational state degraded or better, which a link-local address satisfies. A managed link with no cable, or with a cable and no DHCP answer, stays configuring and blocks it to the timeout, 120 seconds by default. Measured at --timeout=10: d0: link is being processed by networkd: setup state is configuring. Timeout occurred while waiting for network connectivity. And since version 254 a machine where networkd manages nothing also blocks to the timeout, because the program requires at least one managed link, which the 253 release notes still say it does not.

Netplan, since 1.0.1, rewrites that unit. Its drop-in replaces ExecStart with two runs: first every non-optional interface to degraded, then --any to routable. Two consequences follow. A second NIC with no cable holds the boot for the full timeout unless its stanza says optional: true, so that word is not cosmetic. And the drop-in carries a ConditionPathIsSymbolicLink= on an enablement symlink that netplan creates only when a non-optional networkd interface exists. Name an interface that is not there, and wait-online is skipped: network-online.target is reached at once, and there is no network. The 26.04 unit is called “Wait for Network to be Online”; 24.04’s says “Configured”, so grep the journal for wait-online, not the description.

NetworkManager’s definition is nm-online -s: “startup complete”, which its own unit file describes as devices and profiles having “reached a conclusive activated or deactivated state”. A profile that conclusively failed to get an address counts. The timers page’s advice, After=network-online.target “do not start until the network is genuinely up”, should read: do not start until the network manager says it has finished. That is not the same thing, and this page is where the difference lives. systemd, Explained has the target; The Life of a Scheduled Job has the job that trusted it.

Stage 6 — The change

Two managers on one interface do not fight. That sentence, on this site’s ip page, said their settings “revert every few seconds”, and the measurement says otherwise. networkd and NetworkManager were put on the same link. Ten seconds later it had two addresses, .168 from NetworkManager at metric 100 and .169 from networkd at metric 1024, two default routes, two leases on the server for one MAC, and both daemons reporting success. Neither reasserts on a timer. Each is an event-driven writer that trusts the kernel between events. It rewrites the interface only at its own next one: a renewal at T1, half the lease; a carrier flap; netplan apply or networkctl reconfigure; nmcli connection up; a reboot.

$ networkctl reconfigure eth1; sleep 23; ip -4 -br addr show eth1
eth1@if13        UP             10.78.0.169/24 metric 1024
$ nmcli -f GENERAL.STATE,IP4.ADDRESS device show eth1
GENERAL.STATE:                          100 (connected)
IP4.ADDRESS[1]:                         10.78.0.169/24
$ nmcli connection up "Wired connection 1"; sleep 4; ip -4 -br addr show eth1
eth1@if13        UP             10.78.0.168/24
$ networkctl status eth1 | grep -E 'State|Address'
                   State: routable (configured)
                 Address: 10.78.0.168
$ grep ADDRESS= /run/systemd/netif/leases/14
ADDRESS=10.78.0.169

Read the two status lines. After networkd’s reconfigure removed NetworkManager’s address, NetworkManager reported connected with networkd’s address as its own, and 23 seconds later had not put its own back. After NetworkManager reactivated and removed networkd’s, networkctl status said routable (configured) and Address: 10.78.0.168, while networkd’s own lease file said .169. Nothing logged either removal at the default level. The same events decide the fate of an address you added by hand. ip addr add survives until NetworkManager next activates the profile, and a carrier flap is enough. Under networkd it survives until the next configure, which a flap is not but reconfigure and netplan apply are. So a “temporary” address added to reach a rescue host lasts weeks, and then vanishes during an unrelated change.

Carrier loss is the sharpest event. networkd logs Lost carrier, then DHCP lease lost, and removes the address and routes at once; NetworkManager goes activated -> unavailable (reason 'carrier-changed') and the address is gone with it. Since kernel 6.2 an interface can be renamed while it is up, so a live ip link set eth0 name x succeeds where 5.15 refused it. The kernel then marks the name as user-assigned, and the keep policy will never rename it again. Every one of these is one party editing the kernel and updating only its own notes, which is why the last stage exists.

Stage 7 — The record

Every hand in the seven stages above wrote down what it did, in its own place, and none of those places is the kernel. When the kernel’s state and a record disagree, the record’s owner is the party you are looking for.

PartyWhere it wroteWhat only it knows
udevudevadm info /sys/class/net/X (the database under /run/udev/data)every name candidate, the scheme version, which .link applied, INTERFACE_OLD
the initramfsthe .link copies inside the image: lsinitramfs or lsinitrd, grep systemd/networkwhich .link actually ran at the rename
cloud-init/var/lib/cloud/instance/network-config.json, /etc/netplan/50-cloud-init.yaml, /var/log/cloud-init.logwhich datasource, which event, which MAC-to-name map. Azure, GCE and OpenStack re-render every boot; every datasource re-applies set-name
netplan/run/systemd/network/10-netplan-*, /run/NetworkManager/system-connections/netplan-*, /run/udev/rules.d/9?-netplan*, the wait-online drop-inwhat the YAML rendered to, including the metric and MTU settings you never wrote
systemd-networkd/run/systemd/netif/links/N and leases/Nits lease versus the kernel’s address; DHCP4 Client ID in networkctl status
NetworkManager/run/NetworkManager/system-connections/ (the auto-created “Wired connection N”), its lease files, journalctl -u NetworkManagersys-iface-state: 'assume' versus 'managed': whether it adopted the kernel’s configuration or made it
the DHCP client/var/lib/dhcpcd/duid and *.lease; /var/lib/dhcp/dhclient*.leases; udhcpc nothingthe identity it will present next time. dhcpcd’s DUID kept the MAC udev had already replaced
the DHCP serverdnsmasq.leases, Kea’s kea-leases4.csv, dhcpd.leasesthe binding of identity to address: the only durable record, on another machine, and it outlives yours

A worked diagnosis

Back to the backup job from the top of the page. The server is Ubuntu 24.04 with netplan 1.1.2, and /etc/netplan/50-cloud-init.yaml was written years ago on another hypervisor: an ens3 stanza with dhcp4: true and no match, because on that hypervisor the virtual PCI device exposed a slot index. The VM was migrated to a host whose topology exposes none, and rebooted. systemctl status systemd-networkd-wait-online says:

Condition check resulted in Wait for Network to be Configured being skipped.

The first message, unresolvable host, pointed at DNS. The second points at a unit that decided not to run. Neither names a stage. The hinge does:

$ ip -br link; ip -br -4 addr
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enp0s3           UP             52:54:00:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
lo               UNKNOWN        127.0.0.1/8
$ networkctl list
IDX LINK   TYPE     OPERATIONAL SETUP
  1 lo     loopback carrier     unmanaged
  2 enp0s3 ether    carrier     unmanaged
$ udevadm info /sys/class/net/enp0s3 | grep -E 'ID_NET_(NAME|LINK|NAMING)'
E: ID_NET_NAMING_SCHEME=v255
E: ID_NET_NAME_MAC=enx525400123456
E: ID_NET_NAME_PATH=enp0s3
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: ID_NET_NAME=enp0s3

Line one: the kernel has one NIC, up, with carrier, and no IPv4 line. Not 169.254; nothing. Line two: unmanaged, stage 2. Nobody claimed it, and the YAML is for an interface that is not on this machine. Line three, stage 1: udev computed a path name only. There is no ID_NET_NAME_SLOT, so the slot policy yielded nothing and path won, enp0s3. The record that explains why the slot vanished is not on this machine; it is the old hypervisor’s PCI topology. The record that explains why nothing waited is /run/systemd/generator/network-online.target.wants/, which has no systemd-networkd-wait-online.service symlink because netplan found no non-optional interface to wait for, so its condition failed and the target was reached with nothing behind it. On the GA 24.04 image, before netplan 1.0.1, or on bare networkd, the same YAML gives the other shape. Two minutes on the console at “A start job is running for Wait for Network to be Configured”, then the same failure.

The fix is to stop matching on a name that is an output. match: {macaddress: "52:54:00:12:34:56"} with set-name: ens3 if scripts need the old name, which is cloud-init’s own shape; then netplan apply, and expect a reboot before the primary name changes, because the rename is an add-event action. Then make the boot honest: optional: true on any NIC that may be absent, and for the job, Requires=systemd-networkd-wait-online.service, so a wait that times out fails the job instead of releasing it. A skipped wait passes its condition check as far as dependencies are concerned; only the corrected match prevents that. The shape is the one to remember. The message named an outcome inside the job’s field of view. The decision, unmanaged, was taken by a generator reading a name that a different party had changed, for a reason recorded on a machine that no longer exists.

Symptom, stage, command

What you seeStageWhat names it
No network after a migration, upgrade or new kernel; the config names ens3, the machine has enp0s31udevadm info /sys/class/net/X | grep ID_NET_NAME: which candidates exist, and ID_NET_NAMING_SCHEME
A new .link file does nothing until a reboot, and then nothing1lsinitramfs or lsinitrd for systemd/network: the initramfs copy is what ran
A veth or bridge has a different MAC from the one the kernel gave it1cat /sys/class/net/X/addr_assign_type; 3 means udev’s MACAddressPolicy=persistent
Both managers active; you cannot tell who owns the link2networkctl list SETUP and nmcli device status STATE for the same link
Two addresses, two default routes, two leases for one MAC2both columns claim it: two managers
Interface UP, carrier, no IPv4 address, no 169.2543the server’s lease table, and networkctl status X for DHCP4 Client ID
A different address after switching between NetworkManager and networkd3the live lease carries a different client identifier: compare option 61 in the server’s leases
The default route vanished at a renewal after a DHCP server change4the lease now carries option 121; the client dropped option 3
Search domains from a network you do not trust are being applied4resolvectl status; Ubuntu 24.04 networkd’s UseDomains=yes patch
A boot waits two minutes on “Wait for Network”5journalctl -b -u systemd-networkd-wait-online: which link stayed configuring, or nothing managed at all
network-online.target active, job failed, no network5systemctl status systemd-networkd-wait-online: skipped by condition
An address you added by hand disappeared weeks later6the other writer’s event: journalctl -u NetworkManager for a reactivation, or a netplan apply in the shell history
networkctl status shows an address networkd did not give7/run/systemd/netif/leases/N: ADDRESS= is what networkd was given; the status line is what the kernel has

Before you call it done

  • ip -br link; ip -br -4 addr — the kernel’s answer, before anybody’s status command.
  • networkctl list; nmcli device status — one owner per link, and unmanaged where you expect it.
  • udevadm info /sys/class/net/X | grep -E 'ID_NET_(NAME|LINK|NAMING)' — the candidates, the scheme, and the .link that applied; on ≤255 never udevadm test.
  • grep -h . /etc/netplan/*.yaml — every physical NIC matched by macaddress, and optional: true on any that may be absent.
  • systemctl cat systemd-networkd-wait-online — read netplan’s drop-in, and know whether it will wait, skip or stall.
  • ip route — one default, and you know whose metric it is.
  • grep ADDRESS= /run/systemd/netif/leases/* against ip -br -4 addr — the lease and the kernel agree.
  • On the DHCP server, the lease for this MAC has one client identifier, and it is the one this machine will present after its next reinstall.

Related reading

Leave a Reply

Your email address will not be published. Required fields are marked *