Bug report
After upgrading an Ubuntu 26.04 (resolute) OpenStack VM from cloud-init 26.1-0ubuntu2 to the SRU 26.1-0ubuntu3~26.04.1, the first reboot never completes: the network stage blocks forever inside wait_for_network() on systemctl start systemd-networkd-wait-online.service. cloud-init-network.service ships TimeoutSec=0, and ssh.socket/docker.service are ordered after cloud-init, so the host ends up answering ICMP with zero listeners — effectively bricked (only provider-console/disk surgery recovers it). The hang reproduced deterministically on every boot attempt (4/4), including after a full stop/start of the VM.
The same image with 26.1-0ubuntu2 (fresh VM, same cloud, same datasource) executes the identical call in 0.125 s — systemd starts networkd alongside wait-online and boot proceeds.
Environment
- Ubuntu 26.04 LTS (resolute) cloud image, OpenStack provider (
DMI: OpenStack Foundation OpenStack Nova), ConfigDrive (config-2) present, netplan renderer, single NIC (ens3, DHCP4)
- Broken boot: cloud-init
26.1-0ubuntu3~26.04.1, systemd 259.5-0ubuntu3.4, kernel 7.0.0-30-generic (all three landed in one apt-get upgrade/dist-upgrade three days before the first reboot — so I cannot fully isolate cloud-init vs. systemd; the behavioral delta is in this code path)
- Working boot (for comparison): same image, cloud-init
26.1-0ubuntu2, systemd 259.5-0ubuntu3, kernel 7.0.0-15-generic
Evidence (from the dead host's disk, post-mortem)
/var/log/cloud-init.log — these are the final lines; nothing follows for 28+ minutes until the VM was power-cycled:
main.py[DEBUG]: Will wait for network connectivity before continuing
stages.py[DEBUG]: Using distro class <class 'cloudinit.distros.ubuntu.Distro'>
activators.py[DEBUG]: Using selected activator: <class 'cloudinit.net.activators.NetplanActivator'> from priority: ['netplan', 'eni', 'network-manager', 'networkd']
subp.py[DEBUG]: Running command ['systemctl', 'is-enabled', 'NetworkManager.service'] with allowed return codes [0] (shell=False, capture=True)
subp.py[DEBUG]: Running command ['systemctl', 'start', 'systemd-networkd-wait-online.service'] with allowed return codes [0] (shell=False, capture=True)
Journal of the same boot: after Starting cloud-init-network.service - Cloud-init: Network Stage... at uptime ~6.5 s, systemd logs zero further job transitions for the rest of the boot (26+ min observed). systemd-networkd.service is never started in the main system (it ran only in the initrd, which got a DHCP lease fine and handed off). No Ordering cycle/Deleting job messages. The console shows only:
[*** ] Job cloud-init-network.service/start running (9min 2s / no limit)
So the start job for systemd-networkd-wait-online.service (BindsTo=/After=systemd-networkd.service) is enqueued but never becomes runnable, networkd's pulled-in job never runs either, and since the caller is the unit the rest of boot is ordered behind (Before=sysinit.target), the wait can never be satisfied — a deadlock that systemd's transaction cycle-checker cannot see because the jobs arrive in a second transaction.
On the working 26.1-0ubuntu2 boot, the same sequence logs:
subp.py[DEBUG]: Running command ['systemctl', 'start', 'systemd-networkd-wait-online.service'] with allowed return codes [0] (shell=False, capture=True)
performance.py[DEBUG]: Running ['systemctl', 'start', 'systemd-networkd-wait-online.service'] took 0.125 seconds
with the journal showing Starting systemd-networkd.service... immediately after Starting cloud-init-network.service....
Impact
A routine SRU upgrade turned into an unbootable production host on its next (unrelated, provider-initiated) reboot. Because cloud-init-network.service has TimeoutSec=0, there is no bound at all: sshd never starts, and the machine cannot be recovered without out-of-band access.
Workarounds we validated
- Drop-in on
cloud-init-network.service with TimeoutStartSec=300 — the stage's job fails at the bound and boot proceeds (sshd/docker come up; After= is satisfied by a finished job). This seems worth shipping as a belt-and-braces default given the blast radius of TimeoutSec=0.
- Booting with
cloud-init=disabled also recovers the host.
Suggested angle
wait_for_network() issuing a blocking systemctl start from inside a unit that gates sysinit.target means any condition that keeps the started unit's job queued (rather than failing fast) converts to an infinite boot hang. Either the call needs its own timeout, or cloud-init-network.service should not ship TimeoutSec=0, or the wait should verify systemd-networkd.service is active/activatable before blocking on wait-online.
I no longer have the dead disk attached (provider reclaimed it), but I extracted full journals and logs for the broken and working boots before it went and can share more excerpts on request.
Bug report
After upgrading an Ubuntu 26.04 (resolute) OpenStack VM from cloud-init
26.1-0ubuntu2to the SRU26.1-0ubuntu3~26.04.1, the first reboot never completes: the network stage blocks forever insidewait_for_network()onsystemctl start systemd-networkd-wait-online.service.cloud-init-network.serviceshipsTimeoutSec=0, andssh.socket/docker.serviceare ordered after cloud-init, so the host ends up answering ICMP with zero listeners — effectively bricked (only provider-console/disk surgery recovers it). The hang reproduced deterministically on every boot attempt (4/4), including after a full stop/start of the VM.The same image with
26.1-0ubuntu2(fresh VM, same cloud, same datasource) executes the identical call in 0.125 s — systemd starts networkd alongside wait-online and boot proceeds.Environment
DMI: OpenStack Foundation OpenStack Nova), ConfigDrive (config-2) present, netplan renderer, single NIC (ens3, DHCP4)26.1-0ubuntu3~26.04.1, systemd259.5-0ubuntu3.4, kernel7.0.0-30-generic(all three landed in oneapt-get upgrade/dist-upgradethree days before the first reboot — so I cannot fully isolate cloud-init vs. systemd; the behavioral delta is in this code path)26.1-0ubuntu2, systemd259.5-0ubuntu3, kernel7.0.0-15-genericEvidence (from the dead host's disk, post-mortem)
/var/log/cloud-init.log— these are the final lines; nothing follows for 28+ minutes until the VM was power-cycled:Journal of the same boot: after
Starting cloud-init-network.service - Cloud-init: Network Stage...at uptime ~6.5 s, systemd logs zero further job transitions for the rest of the boot (26+ min observed).systemd-networkd.serviceis never started in the main system (it ran only in the initrd, which got a DHCP lease fine and handed off). NoOrdering cycle/Deleting jobmessages. The console shows only:So the start job for
systemd-networkd-wait-online.service(BindsTo=/After=systemd-networkd.service) is enqueued but never becomes runnable, networkd's pulled-in job never runs either, and since the caller is the unit the rest of boot is ordered behind (Before=sysinit.target), the wait can never be satisfied — a deadlock that systemd's transaction cycle-checker cannot see because the jobs arrive in a second transaction.On the working
26.1-0ubuntu2boot, the same sequence logs:with the journal showing
Starting systemd-networkd.service...immediately afterStarting cloud-init-network.service....Impact
A routine SRU upgrade turned into an unbootable production host on its next (unrelated, provider-initiated) reboot. Because
cloud-init-network.servicehasTimeoutSec=0, there is no bound at all: sshd never starts, and the machine cannot be recovered without out-of-band access.Workarounds we validated
cloud-init-network.servicewithTimeoutStartSec=300— the stage's job fails at the bound and boot proceeds (sshd/docker come up;After=is satisfied by a finished job). This seems worth shipping as a belt-and-braces default given the blast radius ofTimeoutSec=0.cloud-init=disabledalso recovers the host.Suggested angle
wait_for_network()issuing a blockingsystemctl startfrom inside a unit that gatessysinit.targetmeans any condition that keeps the started unit's job queued (rather than failing fast) converts to an infinite boot hang. Either the call needs its own timeout, orcloud-init-network.serviceshould not shipTimeoutSec=0, or the wait should verifysystemd-networkd.serviceis active/activatable before blocking on wait-online.I no longer have the dead disk attached (provider reclaimed it), but I extracted full journals and logs for the broken and working boots before it went and can share more excerpts on request.