6.0 KiB
name, description, version, author, platforms, metadata
| name | description | version | author | platforms | metadata | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| reboot-with-health-check | Reboot any server with automated post-reboot verification — SSH availability, hostname check, and configurable service port health checks. | 1.2.0 | ShoNuff |
|
|
Reboot With Health Check
Never reboot a server blind. Always use this procedure to ensure the server and its services come back fully before reporting success or proceeding.
Script
/root/.hermes/scripts/reboot-with-check.sh
Usage:
# Reboot a server, verify SSH comes back
bash reboot-with-check.sh <hostname|ip>
# Reboot a web server, verify SSH + HTTP + HTTPS come back
bash reboot-with-check.sh 5.161.62.38 80 443
# Reboot a database server, verify SSH + PostgreSQL
bash reboot-with-check.sh 178.156.130.130 5432
# Reboot with SSH + multiple ports
bash reboot-with-check.sh app1-bu.itpropartner.com 22
Procedure
HARD PRECONDITION — user-approved policy (Jul 5, 2026):
Before any reboot, shutdown, or power-cycle command:
- RESOLVE any opaque server identifier (Hetzner API ID, etc.) to hostname + IP
- STATE both hostname and IP to the user
- FLAG if the server runs active services (N8N, Postgres, LLM proxy, Docker containers)
- WAIT for explicit confirmation before executing
Resolution example:
python3 -c "
import urllib.request, json
token = open('/root/.hermes/scripts/.hetzner_token').read().strip()
s = json.loads(urllib.request.urlopen(
urllib.request.Request('https://api.hetzner.cloud/v1/servers/<ID>',
headers={'Authorization': f'Bearer {token}'})
).read())['server']
print(f\"Target: {s['name']} ({s['public_net']['ipv4']['ip']})\")
print(f\"Status: {s['status']}\")
"
Then state: "Rebooting [HOSTNAME] ([IP])" and wait for user confirmation. Never rely on mental memory of server IDs — they are opaque integers that look nothing like hostnames.
- Declare intent. State the target hostname AND IP. Do NOT rely on memory of server IDs.
- Confirm with user. If the server runs active services (N8N, database, LLM proxy), flag it before proceeding.
- Trigger reboot via SSH or Hetzner API
- Confirm server goes down — 2 failed pings
- Wait for SSH to come back (up to 120s timeout)
- Brief buffer (5s) for post-reboot stabilization
- Verify hostname — reject if still in rescue mode
- Verify each service port specified (HTTP 80, HTTPS 443, etc.)
- Report result — success or specific failure
Prerequisites
Before invoking any reboot, you MUST have the target server's hostname and IP address directly verifiable — not just a server ID or mental note. Hetzner API server IDs are opaque integers that look nothing like their hostnames.
Hard rule: State the hostname AND IP to the user before triggering any reboot, shutdown, or power-cycle command. Wait for confirmation if the server runs active services (N8N, databases, LLM proxy, Docker containers with state).
See references/destructive-action-protocol.md in the infrastructure-automation umbrella for the full user-approved policy.
Hard preconditions for ANY destructive action
Before ANY destructive or infrastructure-affecting operation (reboot, shutdown, power-cycle, DNS change, config deploy, service restart, firewall rule change, container destroy, API key rotation), you MUST:
- Verify the target — resolve opaque identifiers to hostname + IP. For Hetzner API calls, run the server resolution before the action. Never use mental memory.
- State hostname + IP to the user in the message before executing.
- Flag risk — if the target runs active services (N8N, Postgres, LLM proxy, Docker containers with state), say so explicitly and wait for confirmation.
- Do not rely on server IDs alone — they are visually indistinguishable from one another.
This applies to ALL tools: terminal, write_file (config files), patch (production configs), curl (API POST/DELETE), cronjob (modify), and any script that changes live state.
Pitfalls
- WRONG SERVER DISASTER. Accidentally rebooted
app1.itpropartner.com(N8N host, ID 127781917, IP 87.99.144.163) thinking it wasapp1-bu(standby, ID 125997675, IP 5.161.114.8). The IDs are close enough to confuse in memory (127781917 vs 125997675) but one runs N8N+Postgres and the other is a dormant standby. What should have happened: never reference servers by Hetzner API ID alone. Resolve the ID to hostname + IP, then state BOTH to the user before any power-cycle. Verifiable precondition:python3 -c "import urllib.request,json; s=json.loads(urllib.request.urlopen(urllib.request.Request('https://api.hetzner.cloud/v1/servers/<ID>', headers={'Authorization':'Bearer ...'})).read())['server']; print(s['name'], s['public_net']['ipv4']['ip'])"before any destructive action. Applies to app1-bu (standby), app1.itpropartner.com (N8N+Postgres), ai.itpropartner.com (LiteLLM proxy), and any future Hetzner box. - Always verify the server is NOT booted into rescue mode (hostname check)
- Don't assume SSH = all services — specify critical service ports in the check
- For rescue-mode reboots, disable rescue FIRST, THEN reboot
- The script uses
itpp-infrakey by default path — if a different key is needed, specify it - AI/LLM proxy servers (admin-ai.itpropartner.com): Rebooting your active LLM provider takes the agent offline. Before rebooting, either (a) have a local fallback model (ollama) installed and configured, or (b) have the user ready to restart the proxy manually.
- app1.itpropartner.com (87.99.144.163) has an intermittent SSH delay — port 80 responds quickly but SSH can take an extra 30-60s after the Hetzner API reports
running. - Hetzner
enable_rescueSSH key injection is server-type dependent. Works on CPX11/CPX21, silently fails on CPX41. Workaround: use the rescue root password or have a user add the key manually. - Hostname briefly says
rescueafter disabling rescue and rebooting. Wait for normal SSH to return.