scope sleep to homelab, and port the boot hang fix
Sleep and suspend is homelab only now. The two exclusions are for different reasons and both are stated in the run rather than left implicit: dev — a laptop should sleep; disabling it is a hot bag and a flat battery. vps — not merely unnecessary, harmful. A virtual machine has no lid and no power button, but the provider's Shut down control works by sending an ACPI power button event. HandlePowerKey=ignore makes the VM ignore it, so graceful shutdown requests silently do nothing and the instance is hard-killed instead. systemd defaults that key to poweroff for exactly this reason. The boot hang fix is everything except vps, where systemd-networkd genuinely manages the network and the unit is load-bearing. Rather than asking whether boot "feels slow" — a question people answer from memory of the worst time it happened — the step prints what the unit actually cost on this boot, from systemd's own accounting. On this host that is 14ms, which ends the discussion. On a NetworkManager desktop it is two minutes, which also ends it. On dev the wording says outright that a small number here means there is nothing to do. The original's live guard is kept and is what actually decides: NetworkManager active and networkd not. Anything else, including "cannot tell", is left alone, and the reason is printed. The warning against disabling systemd-networkd outright is carried across into the library, where the alternative would be attempted. Verified all three roles on this host, which is a networkd machine: homelab and dev both correctly refuse and explain, vps skips as not applicable, and the timing helper reads 14ms out of systemd-analyze. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -438,6 +438,52 @@ disable_sleep() {
|
||||
systemctl restart systemd-logind
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Boot hang
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
# systemd-networkd-wait-online blocks boot until the network is up. Where
|
||||
# systemd-networkd actually manages the network — a server or cloud image, via
|
||||
# cloud-init and netplan — it does that in milliseconds and is load-bearing.
|
||||
#
|
||||
# Where NetworkManager owns the network instead, systemd-networkd runs nothing,
|
||||
# but the wait-online unit is still enabled and waits for a link that will never
|
||||
# be configured. It gives up after its full timeout, on every boot.
|
||||
#
|
||||
# So the fix is masking one unit, and only on the second stack. Do NOT
|
||||
# `systemctl disable --now systemd-networkd` to achieve the same thing: on a
|
||||
# networkd-managed box that brings it up with no network on the next boot, no
|
||||
# ssh, and nothing but the provider's rescue console.
|
||||
|
||||
WAIT_ONLINE_UNIT=systemd-networkd-wait-online.service
|
||||
|
||||
network_manager_name() {
|
||||
if systemctl is-active --quiet NetworkManager.service 2>/dev/null; then
|
||||
echo "NetworkManager"
|
||||
elif systemctl is-active --quiet systemd-networkd.service 2>/dev/null; then
|
||||
echo "systemd-networkd"
|
||||
else
|
||||
echo "neither — unclear"
|
||||
fi
|
||||
}
|
||||
|
||||
# Is the wait actually pointless here? NetworkManager in charge and networkd not.
|
||||
# Anything else, including "cannot tell", is left alone.
|
||||
wait_online_is_spurious() {
|
||||
systemctl is-active --quiet NetworkManager.service 2>/dev/null &&
|
||||
! systemctl is-active --quiet systemd-networkd.service 2>/dev/null
|
||||
}
|
||||
|
||||
# What that unit actually cost on this boot, straight from systemd's own
|
||||
# accounting. Worth printing rather than asking somebody whether boot "feels
|
||||
# slow": the answer is either 14ms or two minutes, and there is no arguing with
|
||||
# it. Empty when the unit did not run.
|
||||
wait_online_boot_time() {
|
||||
systemd-analyze blame 2>/dev/null | awk -v u="$WAIT_ONLINE_UNIT" '$NF == u { $NF = ""; sub(/[[:space:]]+$/, ""); print; exit }'
|
||||
}
|
||||
|
||||
mask_wait_online() { systemctl mask --now "$WAIT_ONLINE_UNIT" >/dev/null 2>&1; }
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Timezone
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -622,8 +622,16 @@ fi
|
||||
# =============================================================================
|
||||
|
||||
step "Sleep and suspend"
|
||||
# A laptop should sleep. Disabling it on the machine somebody carries around is
|
||||
# how you get a hot bag and a flat battery, so dev is skipped — visibly.
|
||||
# Homelab only, and the two exclusions are for different reasons.
|
||||
#
|
||||
# dev: a laptop should sleep. Disabling it on the machine somebody carries is how
|
||||
# you get a hot bag and a flat battery.
|
||||
#
|
||||
# vps: not merely unnecessary but harmful. A virtual machine has no lid and no
|
||||
# power button, but the provider's "Shut down" control works by sending an ACPI
|
||||
# power button event — HandlePowerKey=ignore makes the VM ignore it, so a
|
||||
# graceful shutdown request does nothing and the provider hard-kills the instance
|
||||
# instead. systemd defaults that key to poweroff for exactly this reason.
|
||||
if ! skip && is_role dev; then
|
||||
echo ""
|
||||
info "Sleep and suspend — left alone on a ${MACHINE_ROLE} machine"
|
||||
@@ -631,6 +639,16 @@ if ! skip && is_role dev; then
|
||||
echo " to be stopped from it."
|
||||
SUMMARY+=("Sleep: left alone on ${MACHINE_ROLE}")
|
||||
step_ok
|
||||
elif ! skip && is_role vps; then
|
||||
echo ""
|
||||
info "Sleep and suspend — left alone on a ${MACHINE_ROLE}"
|
||||
echo " A virtual machine has no lid and no power button to disable. What it"
|
||||
echo " does have is the provider's Shut down control, which works by sending"
|
||||
echo " an ACPI power button event — telling this machine to ignore that would"
|
||||
echo " mean graceful shutdowns silently do nothing and the instance gets"
|
||||
echo " hard-killed instead."
|
||||
SUMMARY+=("Sleep: left alone on ${MACHINE_ROLE} — would break provider shutdown")
|
||||
step_ok
|
||||
elif ! skip; then
|
||||
echo ""
|
||||
info "Sleep and suspend — stop this machine putting itself to sleep"
|
||||
@@ -667,13 +685,69 @@ elif ! skip; then
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# 13. Boot hang
|
||||
# =============================================================================
|
||||
|
||||
step "Boot hang"
|
||||
# Not on a VPS. There the network IS systemd-networkd — cloud-init and netplan
|
||||
# put it in charge — so the unit below is load-bearing and completes in
|
||||
# milliseconds. Masking it on that stack is how a box comes up with no network
|
||||
# and no ssh.
|
||||
if ! skip && is_role vps; then
|
||||
echo ""
|
||||
info "Boot hang — not applicable on a ${MACHINE_ROLE}"
|
||||
echo " systemd-networkd is what configures the network here, so the unit"
|
||||
echo " this would mask is doing real work."
|
||||
SUMMARY+=("Boot hang: not applicable on ${MACHINE_ROLE}")
|
||||
step_ok
|
||||
elif ! skip; then
|
||||
WAIT_ONLINE_TIME="$(wait_online_boot_time)"
|
||||
|
||||
echo ""
|
||||
info "Boot hang — a unit that can add two minutes to every boot"
|
||||
echo " On a machine where NetworkManager owns the network, systemd-networkd"
|
||||
echo " runs nothing — but systemd-networkd-wait-online is still enabled, and"
|
||||
echo " waits for a link that will never come up. It gives up after its full"
|
||||
echo " timeout, every single boot."
|
||||
echo ""
|
||||
echo " network managed by: $(network_manager_name)"
|
||||
echo " that unit took: ${WAIT_ONLINE_TIME:-it did not run} on this boot"
|
||||
|
||||
if ! wait_online_is_spurious; then
|
||||
echo ""
|
||||
echo " systemd-networkd is live here, so that unit is doing real work."
|
||||
echo " Masking it would be how this machine comes up with no network."
|
||||
SUMMARY+=("Boot hang: nothing to fix — networkd is in charge")
|
||||
else
|
||||
echo ""
|
||||
if is_role dev; then
|
||||
echo " Only worth doing if you actually feel this — if the machine sits"
|
||||
echo " there for a couple of minutes before the login screen. The number"
|
||||
echo " above is the honest answer for this boot; if it is small, there is"
|
||||
echo " nothing here for you."
|
||||
fi
|
||||
echo " This masks systemd-networkd-wait-online.service only. NetworkManager"
|
||||
echo " keeps configuring the network exactly as it does now."
|
||||
if confirm "Mask it?"; then
|
||||
mask_wait_online
|
||||
ok "systemd-networkd-wait-online masked"
|
||||
SUMMARY+=("Boot hang: wait-online masked (NetworkManager stack)")
|
||||
else
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("Boot hang: SKIPPED by request")
|
||||
fi
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# NOT PORTED YET
|
||||
# =============================================================================
|
||||
#
|
||||
# Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order:
|
||||
#
|
||||
# boot-hang fix · user creation ·
|
||||
# user creation ·
|
||||
# ssh keys · ssh hardening · dns · static ip · fail2ban · unattended-upgrades ·
|
||||
# git config · docker · zsh + prompt · tailscale · neovim · js runtimes ·
|
||||
# dev tools · ufw · zshrc
|
||||
|
||||
Reference in New Issue
Block a user