fix(hetzner): ensure cloud-init marker is always written despite early exit (#2747)

Remove `set -e` from userdata script and add an EXIT trap to guarantee
/root/.cloud-init-complete is written even if apt-get or other setup
steps fail. Add `|| true` to apt-get commands for extra resilience.

Previously, the userdata script used `set -e` causing it to abort on
any command failure before reaching the marker write at the end. This
made waitForCloudInit() always time out with "Cloud-init marker not
found, continuing anyway..." adding ~5 minutes to every Hetzner
provisioning.

Fixes #2739

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
A 2026-03-17 23:02:16 -07:00 committed by GitHub
parent 1b978c03ce
commit 133b94939e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -295,11 +295,12 @@ function getCloudInitUserdata(tier: CloudInitTier = "full"): string {
const packages = getPackagesForTier(tier);
const lines = [
"#!/bin/bash",
"set -e",
"export HOME=/root",
"export DEBIAN_FRONTEND=noninteractive",
"apt-get update -y",
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
"# Guarantee the cloud-init marker is written on exit (success, failure, or signal)",
"trap 'touch /home/ubuntu/.cloud-init-complete 2>/dev/null; touch /root/.cloud-init-complete' EXIT",
"apt-get update -y || true",
`apt-get install -y --no-install-recommends ${packages.join(" ")} || true`,
];
if (needsNode(tier)) {
lines.push(`${NODE_INSTALL_CMD} || true`);
@ -313,7 +314,6 @@ function getCloudInitUserdata(tier: CloudInitTier = "full"): string {
lines.push(
"echo 'export PATH=\"$HOME/.local/bin:$HOME/.bun/bin:$PATH\"' >> /root/.bashrc",
"echo 'export PATH=\"$HOME/.local/bin:$HOME/.bun/bin:$PATH\"' >> /root/.zshrc",
"touch /home/ubuntu/.cloud-init-complete 2>/dev/null; touch /root/.cloud-init-complete",
);
return lines.join("\n");
}