10 KiB
Runbook: Auto-arranque de Coolify tras apagón
Garantiza que, después de un corte de luz y al volver a encender el servidor
Proxmox (192.168.0.200, nodo thinkcentre), el LXC 102 (Coolify) y su túnel
de Cloudflare arranquen solos, sin intervención manual.
Causa raíz que resolvió esto (2026-07-08): el LXC 102 no tenía la marca
onboot(por defecto0), así que el host encendía pero el contenedor se quedaba apagado y, con él, todo el stack. Todo lo demás dentro del LXC ya estaba bien encadenado (Dockerenabled, contenedores conrestart=always/unless-stopped,cloudflared.serviceenabled).
Arquitectura de la solución (dos capas)
-
Base nativa de Proxmox —
onboot. El LXC 102 arranca solo al encender el host, gestionado porpve-guests.service(que ya estáenabled):pct set 102 --onboot 1 --startup order=1,up=30 -
Guardián auto-reparable —
coolify-autostart.service. Un servicio systemd oneshot en el host que corre en cada arranque, después depve-guests.service, y garantiza el stack completo:/usr/local/bin/coolify-autostart.sh— script idempotente./etc/systemd/system/coolify-autostart.service— unit (WantedBy=multi-user.target).- Log:
/var/log/coolify-autostart.log.
En cada boot el guardián:
- Verifica que el LXC 102 esté
running(lo arranca si no). - Espera a que Docker responda dentro del LXC (deadline de reloj real,
COOLIFY_MAX_WAIT, por defecto 600 s). - Da un margen de asentamiento (
COOLIFY_SETTLE, 180 s) para que Docker arranque sus propios contenedores, y solo entonces fuerza el arranque de los que falten:coolify-db,coolify-redis,coolify-realtime,coolify,coolify-proxy,cloudflared. - Levanta el
cloudflared.servicede systemd dentro del LXC (segundo conector al mismo túnel). - Comprueba que el túnel realmente llegó a Cloudflare: cuenta los
Registered tunnel connectionde este boot y los escribe en el log.
Sale con código ≠ 0 si algo no se pudo arrancar, para que systemd lo marque
failedyRestart=on-failurereintente (hasta 3 veces por hora).Las dos capas son complementarias:
onboothace el trabajo normal; el guardián es una red de seguridad que además auto-repara (p. ej. un contenedor conrestart=no) y deja log de lo ocurrido tras el apagón.
Presupuestos de tiempo (no los bajes a ciegas)
Medido en el boot del 2026-08-07 16:41: pve-guests tarda 78 s en arrancar
el CT 102, el daemon de Docker dentro del LXC solo responde ~4-5 min después
del encendido, y el último contenedor core (coolify) arranca a los 7 m 40 s.
Por eso:
| Parámetro | Valor | Regla |
|---|---|---|
COOLIFY_MAX_WAIT |
600 s | espera de Docker, por reloj real |
COOLIFY_SETTLE |
180 s | margen antes de forzar arranques |
COOLIFY_PROBE_TIMEOUT |
20 s | timeout duro de cada llamada al LXC |
TimeoutStartSec (unit) |
1200 s | debe superar MAX_WAIT + SETTLE |
COOLIFY_LOG también es sobreescribible, para poder hacer pruebas en seco sin
tocar el log de producción.
Los archivos fuente viven en el repo en scripts/host/ y se instalan con scripts/Install-CoolifyAutostart.ps1.
Instalar / reinstalar
# Instala onboot + guardián y lo prueba una vez (idempotente y seguro)
.\scripts\Install-CoolifyAutostart.ps1 -RunNow
Opciones:
-VerifyOnly— solo reporta estado (onboot, unit, log). No cambia nada.-SkipOnboot— instala solo el guardián, sin tocar la marcaonboot.-RunNow— tras instalar, dispara el guardián una vez y muestra el log.-Uninstall— quita el guardián (dejaonbootintacto).
El instalador empuja los archivos por SSH en base64 (normaliza CRLF→LF), inyecta
el COOLIFY_LXC correcto en el unit, activa onboot, habilita el servicio y
verifica.
Verificar estado (solo lectura)
.\scripts\Install-CoolifyAutostart.ps1 -VerifyOnly
Estado sano esperado:
onboot: 1
startup: order=1,up=30
enabled: enabled
state: active
result: success
timeout: 20min
No basta con enabled. enabled solo dice que arrancará; state/result
dicen si la última ejecución funcionó. Una corrida sana del log termina en
=== coolify-autostart done (failures=0) ===. Si el log se corta justo después
de LXC 102 already running, el guardián murió esperando a Docker.
Log del último arranque:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "tail -n 25 /var/log/coolify-autostart.log"
Probar sin apagar producción
No reinicies el host solo para probar (tumbaría todos los servicios). En su lugar, dispara el guardián manualmente — solo arranca cosas, nunca las detiene:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "systemctl start coolify-autostart.service; systemctl is-active coolify-autostart.service; tail -n 25 /var/log/coolify-autostart.log"
Para validar que el unit está bien formado y en el orden correcto:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "systemd-analyze verify /etc/systemd/system/coolify-autostart.service && systemctl show coolify-autostart.service -p After -p WantedBy"
After debe incluir pve-guests.service; WantedBy debe ser multi-user.target.
Para probar la ruta de fallo (que el guardián corte y deje ERROR en vez de
colgarse), apúntalo a un LXC inexistente con un log temporal:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "env COOLIFY_LXC=999 COOLIFY_LOG=/tmp/ca-test.log COOLIFY_MAX_WAIT=15 COOLIFY_PROBE_TIMEOUT=5 bash /usr/local/bin/coolify-autostart.sh > /dev/null 2>&1; cat /tmp/ca-test.log; rm -f /tmp/ca-test.log"
Debe terminar en ERROR: docker not ready after 15s (wall clock) -> aborting
en ~18 s. No toca producción ni el log real.
Incidente 2026-08-07 — el guardián llevaba 2/2 arranques muriendo
Síntoma: systemctl is-enabled decía enabled, pero la unidad estaba
failed (Result: timeout) en los dos reinicios reales del día (09:47 y 16:41).
El log se cortaba siempre en LXC 102 already running, sin línea de ERROR.
Cronología del boot de las 16:41:
| Hora | Evento |
|---|---|
| 16:41:49 | pve-guests arranca el CT 102 |
| 16:43:07 | termina pve-guests (78 s) y arranca el guardián |
| 16:43:08 | LXC 102 already running → entra a esperar Docker |
| 16:46:05 | Docker empieza a levantar contenedores (coolify-db) |
| 16:48:07 | systemd mata al guardián: TimeoutStartSec=300 |
| 16:48:40 | arranca coolify — 33 s después de que el guardián ya estaba muerto |
Causa raíz (tres defectos que se sumaron):
- El bucle de espera contaba iteraciones de
sleep, no reloj real, así queMAX_WAIT=180no acotaba nada. - Las llamadas
pct exec ... docker infono tenían timeout ydocker infoes caro (enumera los ~50 contenedores): con el daemon saturado en el arranque en frío, una sola llamada se bloqueaba minutos y consumía todo el presupuesto en silencio. TimeoutStartSec=300estaba por debajo del tiempo real de convergencia (~7 m 40 s), así que systemd mataba al guardián antes de que pudiera actuar.
Nunca hubo una ejecución exitosa en un arranque real: la única corrida sana del
log (2026-07-08) fue el -RunNow manual con todo ya arriba.
Por qué no se notó durante un mes: -VerifyOnly solo miraba is-enabled.
Ahora también reporta state, result, timeout y el journal del último boot.
Qué salvó el servicio mientras tanto: las capas base, que sí funcionaron en
los dos reinicios — onboot=1, docker.service enabled, políticas
restart=always/unless-stopped y cloudflared.service enabled (registró sus
4 conectores QUIC a los 3 m 41 s del boot). El guardián es red de seguridad, no
el mecanismo principal; por eso el apagón no se notó de cara al usuario.
Corrección: deadline por reloj real, timeout duro en cada llamada al LXC,
sonda barata docker version en vez de docker info, margen de asentamiento
antes de forzar arranques, verificación de conectores del túnel en el log,
TimeoutStartSec=1200 y Restart=on-failure.
Rollback
# Quitar el guardián (deja onboot como esté)
.\scripts\Install-CoolifyAutostart.ps1 -Uninstall
# Y si además quieres que el LXC deje de arrancar solo:
.\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct set 102 --onboot 0"
Notas
- El guardián es idempotente: correrlo con todo ya arriba no hace nada destructivo, solo lo confirma en el log.
- Hay dos conectores
cloudflaredal mismo túnel (5f0e5c5b-a180-46f2-a090-44d151006a62): el contenedor Dockercloudflared(restart=unless-stopped) y elcloudflared.servicede systemd dentro del LXC (enabled). Ambos arrancan solos; es redundante pero inofensivo. Si algún día se consolida en uno, ajustar el paso 4 del script y esta nota. Ver cloudflare-tunnel.md. coolify-sentineltienerestart=no(monitor no crítico); Coolify lo recrea, por eso no está en la lista de contenedores core del guardián.- El único LXC con
onbootes el 102. El LXC100 hermesno tiene la marca, así que no arranca solo tras un apagón. Es intencional mientras sea secundario; si algún día deja de serlo,pct set 100 --onboot 1.
Verificado: 2026-08-07 — auditoría completa tras dos reinicios reales del
día. Se detectó y corrigió el fallo del guardián (ver incidente arriba). Estado
final: onboot=1, unidad enabled / active / result=success,
TimeoutStartUSec=20min, Restart=on-failure, After incluye
pve-guests.service, systemd-analyze verify sin warnings. Guardián ejecutado
de punta a punta en 9 s con failures=0, 6 contenedores core running,
cloudflared.service active con 4 conectores registrados. Ruta de aborto
por reloj real probada en seco (corta a los 18 s con ERROR). Público
verificado a través del túnel: coolify.urieljareth.org → 302,
chat.urieljareth.org → 200.
Verificado: 2026-07-08 — instalación original (onboot=1 + guardián). La
prueba de entonces fue un -RunNow manual, no un arranque real; de ahí que el
defecto de tiempos no se detectara hasta 2026-08-07.