Restart policy del agente IA: unless-stopped vs crash loop
Resumen
Cómo no pelearte con el kill switch: restart unless-stopped en Compose, on-failure con tope, Fly auto-restart vs scale 0. Un OOM no debe reiniciar 400 veces. Un deploy tampoco debe dejar el bot apagado para siempre. Política escrita, no el default.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
El proceso se cae. Docker lo levanta. Se cae. Lo levanta. A las 2 AM tienes un loop de tokens y una factura. Kill switch quiere que se quede abajo. always ignora tu docker stop. Fuentes oficiales consultadas el 3 de septiembre de 2026.
La regla
Prod VPS: restart: unless-stopped (sobrevive reboot; respeta docker stop).
Crash por bug: on-failure:5 (tope).
Kill switch: scale 0 / stop, no pelees con always.
no (default) = un OOM y el bot queda muerto hasta que alguien mire alertas.
Compose
restart en el servicio. unless-stopped es el default sano para un webhook 24/7. always relanza incluso después de un stop consciente: el kill switch pierde. on-failure no relanza un exit 0; si tu SIGTERM sale 0, no vuelve. Combina con graceful: exit 0 = apagado limpio.
deploy.restart_policy es Swarm. En Compose clásico usa restart:.
docker run
--restart unless-stopped equivalente. --restart on-failure:5 tope. Sin política, el contenedor queda Exited.
Fly / Railway
Fly relanza Machines según health y restart en fly.toml. Un crash loop cuenta. fly scale count 0 es el stop. Railway reinicia el servicio; 0 réplicas es el off. No copies unless-stopped a wrangler: Workers no es un daemon.
OOM y CPU
Si el cgroup mata (OOM), always es un loop de muerte. Tope de restarts + alerta. Sube RAM o recorta RSS; no “más restarts”.
Tabla

| Política | Reboot host | docker stop | Crash | Uso |
|---|---|---|---|---|
| no | muerto | muerto | muerto | solo debug |
| on-failure:N | muerto | muerto | hasta N | jobs |
| unless-stopped | vive | respeta stop | vive | webhook 24/7 |
| always | vive | relanza | vive | casi nunca |
Errores comunes

| Síntoma | Causa | Fix |
|---|---|---|
| Bot vuelve tras kill | always | unless-stopped + scale 0 |
| Muerto tras reboot VPS | no / on-failure | unless-stopped |
| 400 arranques/hora | OOM loop | tope + RAM |
| Fly flapping | health fail | arregla listen (bind) |
exit 0 y no vuelve | on-failure | unless-stopped o exit 1 |
Relación con el resto
- Apagar a propósito: kill switch.
- Muerte limpia: SIGTERM.
- Health: healthchecks.
- Memoria: OOM.
Checklist
- Compose
restart: unless-stopped - No
alwaysen el agente con webhook - Tope si usas on-failure
- Kill switch documentado vs la política
- Alerta si flapping
- Ensayo:
docker stopqueda down; reboot host vuelve
FAQ
¿systemd Restart=always? En VPS sin Compose, on-failure + StartLimitBurst. Mismo criterio.
¿K8s replicas? restartPolicy: Always en Pods es normal; el kill switch es replicas: 0.
¿Workers? No hay restart de proceso. El isolate muere por request.
Delay entre restarts
Docker no espera mucho entre crashes. Un agente que abre sqlite corrupto y muere en 50 ms martilla el disco. Pon backoff en el entrypoint o arregla la causa. Un sleep 2 eterno no es política; es parche. El tope on-failure:5 + alerta es más honesto.
Railway/Fly muestran “restart count” en el dashboard. Si sube en el día, no subas réplicas: concurrencia duplica el loop.
El ensayo: docker stop; ps vacío. Reboot del VPS (staging); el contenedor vuelve. always fallaría el primer check.
Un restart infinito no es resiliencia. Es un dosificador de tokens. Pon tope o arregla el crash.
Si el entrypoint hace while true; do node; done, Compose restart es doble. Quita el while. Un PID 1 que no muere no deja que Docker cuente fallos. El orquestador tiene que ver el exit code.
restart: unless-stopped + un volumen sqlite corrupto = loop eterno más educado. El tope no está. Por eso la alerta de flapping no es opcional: 10 restarts en 10 min es kill switch, no “ya se recupera”.
Techos oficiales (curl 3 sep 2026)
Página Start containers automatically: --restart admite no (default: no relanza), on-failure[:max-retries] (solo exit ≠ 0; no relanza si el daemon se reinicia), always (si lo paraste a mano, vuelve cuando el daemon arranca o cuando haces start), unless-stopped (como always, salvo que un stop manual o equivalente lo deja abajo incluso tras reboot del daemon). Ejemplo canónico: docker run -d --name redis --restart unless-stopped redis. En un contenedor ya corriendo: docker update --restart unless-stopped redis. Para todos los running: docker update --restart unless-stopped $(docker ps -q).
Detalles que la gente se salta:
- La política solo cuenta después de un start exitoso. Si el contenedor nunca llegó a arrancar, no entra en loop de restart. Eso evita un compose roto martillando el daemon.
- Un stop manual ignora la política hasta que el daemon se reinicia o haces start a mano. Evita el loop.
unless-stoppedes el que sigue ignorándola tras el reboot del daemon. - Aplica a contenedores. Swarm usa otra clave (
deploy.restart_policy). - Foreground + restart: el CLI se va cuando el proceso sale, aunque el contenedor siga vivo por la política. El ejemplo oficial imprime 1..5, sale, tu shell vuelve;
docker psmuestra el contenedor restarting conalways. Undocker runsin-dno es el sitio para un webhook 24/7.
La referencia de docker container run confirma el default de --restart: no. --rm limpia el contenedor al exit: no combines --rm con una política de prod; el objeto desaparece.
--stop-timeout no es restart: segundos de espera tras --stop-signal (default SIGTERM, o STOPSIGNAL de la imagen) antes de SIGKILL. -1 = espera indefinida. Eso es graceful, no “cuántas veces vuelvo”.
on-failure:5 es el tope del daemon, no un backoff. Docker no documenta un sleep largo entre intentos. Un sqlite corrupto que muere en 50 ms sigue pegándole al disco cinco veces rápido. El tope corta el sangrado; la alerta de flapping es lo que te despierta.
Kill switch: docker stop + unless-stopped = se queda abajo en reboot del daemon. always = el reboot del daemon lo levanta otra vez. Por eso always pelea con kill switch. En Fly/Railway el equivalente de stop es réplicas 0, no un yaml de Compose.
Siguiente paso: si la política ya es unless-stopped y igual flapea, logs y OOM. Sin runtime: curso.
Lecturas relacionadas
Sigue explorando Deploy y otras piezas para builders.



