Un worker puede recibir SIGTERM mientras todavía tiene una tarea en memoria. Si trata la señal como un simple process.exit(), puede perder un checkpoint, confirmar un mensaje demasiado pronto o dejar una escritura externa en un estado incierto.
En Cloud Run, la respuesta segura es una entrega controlada. Deja de aceptar tareas nuevas, permite que la unidad activa llegue a un punto seguro, guarda el progreso en almacenamiento durable y deja que otra instancia continúe. La comparación entre Service, Job y worker pool en Cloud Run ayuda a elegir el recurso antes de implementar este ciclo.
Contrato de apagado
- SIGTERM cambia el estado del proceso a “deteniéndose”.
- El loop deja de buscar trabajo nuevo.
- La tarea activa guarda su checkpoint fuera del contenedor.
- La cola se confirma después de comprobar el estado durable.
¿Qué garantiza Cloud Run cuando envía SIGTERM?
En 2026, la documentación de Google Cloud dice que Cloud Run envía SIGTERM antes de SIGKILL cuando una instancia se apaga y describe una ventana de 10 segundos para que el proceso termine (Google Cloud, “Container runtime contract”). Trata esa ventana como un límite operativo. No convierte una escritura incompleta en una transacción.
Las instancias de Cloud Run son descartables. El sistema de archivos escribible del contenedor es una capa en memoria, así que no es un lugar para guardar el checkpoint que necesita la siguiente instancia. El progreso debe ir a una base de datos, una cola, un objeto de Cloud Storage u otro sistema que forme parte del contrato de la aplicación (Google Cloud, “What is Cloud Run”).
En Node.js, un listener de señales recibe el nombre de la señal como argumento. Al instalar un listener para SIGTERM, tu código asume la responsabilidad de iniciar el apagado y se elimina el comportamiento predeterminado que terminaría el proceso (Node.js, “Process”). Por eso, el listener solo debe cambiar el estado y detener la espera de trabajo nuevo. El loop principal hace el resto.
Regla operativa: usa SIGTERM para detener la entrada, no para inventar una confirmación. Una tarea marcada como “processing” debe seguir siendo recuperable si el proceso muere antes de su checkpoint.
¿Cómo convertir SIGTERM en una entrega segura?
En 2026, la documentación del entorno de ejecución de Cloud Run recomienda un handler de SIGTERM y menciona tareas de limpieza, como vaciar logs, durante la ventana de apagado (Google Cloud, “Select an execution environment for services”). Para un worker, la limpieza también incluye cerrar la entrada y guardar el trabajo que ya se aceptó.
El ciclo tiene cuatro partes:
- Detén la entrada. Marca el proceso como
stoppingy cancela solo el polling de la cola o la espera bloqueante. No reclames trabajo nuevo después de la señal. - Drena la unidad activa. Deja que la tarea actual llegue a un punto que pueda repetirse con seguridad. Si no puede terminar dentro del límite, déjala sin confirmar para que la cola o el scheduler la reintente.
- Guarda el estado. Almacena fuera del proceso la identidad de la tarea, el paso terminado y la versión de la entrada. Un archivo local o una variable global no bastan.
- Confirma después. Reconoce el mensaje solo después de que el almacenamiento durable confirme la escritura. La siguiente instancia puede encontrar la misma clave y decidir si reanuda, reutiliza la salida o ejecuta el paso que falta.
Este diseño separa la vida del proceso de la vida del trabajo. Un worker puede morir; la tarea no puede depender de que sobreviva la memoria de ese worker. Si el proceso ejecuta un agente de IA, la misma regla se aplica a las tool calls que crean efectos externos. RemoteCode es un ejemplo de la herramienta del autor para flujos donde el estado y la evidencia deben salir del loop local cuando la ejecución deja el workspace.
Cuando ese checkpoint debe sobrevivir a fallos, aprobaciones y varios pasos, la explicación sobre ejecución durable para agentes de IA profundiza en la diferencia entre una cola, un checkpoint y un workflow.
¿Cómo implementar el apagado en un worker de TypeScript?
El ejemplo muestra el esqueleto del loop. claimNext, processOne, saveCheckpoint y ack son adaptadores ilustrativos. Deben proporcionar lease, escrituras condicionales y una confirmación compatible con tu cola.
import process from "node:process";
type WorkItem = { id: string; inputVersion: string };
let stopping = false;
const stopPolling = new AbortController();
const active = new Set<Promise<void>>();
process.once("SIGTERM", () => {
stopping = true;
stopPolling.abort();
console.log(JSON.stringify({ event: "shutdown_requested" }));
});
async function workerLoop() {
while (!stopping) {
const item = await claimNext({ signal: stopPolling.signal });
if (!item) break;
const run = runItem(item);
active.add(run);
void run.finally(() => active.delete(run));
}
await Promise.all(active);
console.log(JSON.stringify({ event: "shutdown_complete" }));
}
async function runItem(item: WorkItem) {
const result = await processOne(item);
await saveCheckpoint({
taskId: item.id,
inputVersion: item.inputVersion,
result,
});
await ack(item.id);
}
void workerLoop().catch((error) => {
console.error(JSON.stringify({ event: "worker_failed", error: String(error) }));
process.exitCode = 1;
});
Lo importante no es el Set en sí. Es el orden. El handler bloquea nuevos claims, active representa el trabajo aceptado y saveCheckpoint ocurre antes de ack. Si processOne falla o el proceso muere entre las dos últimas operaciones, el mensaje sigue siendo recuperable. La escritura debe ser idempotente o condicional para que un retry no cree una segunda salida.
No uses process.exit() para acortar el camino. La documentación de buenas prácticas de Cloud Run advierte que la actividad en segundo plano después de terminar una invocación puede dejar de recibir CPU y provocar conexiones reiniciadas (Google Cloud, “Functions best practices”). Deja que la función principal termine después de esperar el trabajo que todavía puede completarse de forma segura.
¿Dónde encaja este ciclo: Service, Job o worker pool?
En 2026, el overview de Cloud Run separa Service, Job y worker pool por su modelo de ejecución: un Service atiende HTTP y eventos, un Job ejecuta tareas hasta terminar y un worker pool procesa trabajo continuo basado en pull (Google Cloud, “What is Cloud Run”). El handler de SIGTERM sirve para los tres, pero cambia la frontera del trabajo.
Service
Usa Service cuando una petición o un evento inicia el trabajo. La plataforma da tiempo a las peticiones activas para terminar cuando una instancia debe apagarse, pero tu código todavía debe guardar el estado y evitar actividad que dependa de la instancia después de la respuesta. Para un agente, devuelve un identificador de tarea y mueve el procesamiento largo a una capa con su propia política de recuperación.
Job
Usa Job cuando cada ejecución tiene una entrada definida y debe terminar. Un timeout o una cancelación también pueden enviar SIGTERM a la task. El artículo sobre evitar duplicados en los retries de Cloud Run Jobs cubre la siguiente capa: claves deterministas, checkpoints y escrituras idempotentes. El handler prepara la salida; el diseño del Job decide cómo se reintentará la task.
Worker pool
Usa worker pool cuando el contenedor consume una cola de forma continua y no necesita un endpoint HTTP. La documentación actual dice que los worker pools no escalan a partir de la profundidad de la cola y que las instancias activas siguen generando cargos (Google Cloud, “Container runtime contract”). El worker debe detener el polling, liberar o renovar el lease del mensaje activo y dejar que la siguiente instancia asuma lo que no fue confirmado.
¿Cómo verificar SIGTERM antes del despliegue?
En 2026, Google Cloud documenta una prueba local con Docker: inicia la imagen, envía SIGTERM al contenedor y observa si el proceso termina correctamente (Google Cloud, “Select an execution environment for services”). La prueba debe verificar la entrega, no solo la última línea del log.
docker run --rm --name worker-under-test worker-image:dev
docker kill --signal=SIGTERM worker-under-test
Añade una tarea artificialmente lenta y haz que la prueba confirme cuatro eventos: shutdown_requested, ningún claim nuevo después de la señal, un checkpoint persistido y shutdown_complete. Después inicia un segundo worker con el mismo almacenamiento. Debe encontrar la tarea en un estado recuperable y producir una sola salida para la misma clave.
En el entorno desplegado, correlaciona logs con taskId, inputVersion e identificador de instancia. Comprueba qué ocurrió antes y después de la señal. Una tarea puede haber completado un efecto externo y morir antes de ack. Esa es la ventana que deben cubrir una clave idempotente y una escritura condicional.
Verificación útil: un despliegue verde demuestra que el contenedor inició. No demuestra que una tarea sobrevivió al reemplazo de una instancia. La prueba de recuperación debe detener el proceso en distintos puntos del paso.
¿Qué errores hacen que el apagado pierda trabajo?
El primer error es confirmar la cola antes del checkpoint. Si el proceso muere después de ack, la cola no tiene motivo para entregar la tarea otra vez, aunque la aplicación nunca haya persistido el resultado.
El segundo es cancelar la tarea activa sin definir cómo se reanuda. Abortar una llamada externa puede ser correcto para una operación sin efecto, pero es peligroso para una escritura que ya empezó. Marca el paso como incierto, consulta el estado mediante su clave idempotente y decide si debes repetirlo.
El tercero es escribir solo en el disco local. La capa escribible del contenedor es descartable y puede desaparecer con la instancia. La salida final, el checkpoint y la información del lease deben vivir en un lugar que otra instancia pueda leer.
El cuarto es tratar 10 segundos como una garantía. Google Cloud también documenta la terminación forzada cuando un contenedor supera el límite de memoria. Node.js no puede capturar SIGKILL. Por eso, el sistema debe seguir siendo seguro cuando el handler no consigue terminar.
El quinto es ocultar el proceso detrás de un shell que no reenvía señales. Prefiere un entrypoint que ejecute directamente el proceso principal, como ENTRYPOINT ["node", "dist/worker.js"], y verifica la imagen localmente. Si un script administra subprocesses, también debe reenviar SIGTERM.
Preguntas frecuentes sobre SIGTERM en Cloud Run
¿SIGTERM garantiza que la tarea actual terminará?
No. En 2026, la documentación de Cloud Run describe una ventana de 10 segundos antes de SIGKILL, pero una task puede superar el límite, sufrir un fallo de memoria o detenerse antes del checkpoint. La tarea debe seguir siendo recuperable mediante la cola, la base de datos u otro mecanismo de estado durable.
¿Debo llamar a process.exit() en el handler?
No como primera respuesta. Un listener de SIGTERM elimina el comportamiento de terminación predeterminado de Node.js. Deja que el loop detenga la entrada, espera el trabajo seguro y define process.exitCode solo para fallos que la aplicación no pudo tratar. La función principal debe terminar de forma natural.
¿Qué cambia para un worker pool?
Un worker pool no recibe una petición HTTP que marque el final de una unidad de trabajo. El loop debe detener el pull, terminar o devolver el elemento activo y guardar su checkpoint antes de salir. Como el pool no escala a partir de la profundidad de la cola, la capacidad y la recuperación también deben poder observarse.
Fuentes consultadas
- Google Cloud, “Container runtime contract”, consultado el 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/container-contract - Google Cloud, “Select an execution environment for services”, consultado el 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/configuring/execution-environments?hl=en - Google Cloud, “What is Cloud Run”, consultado el 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/overview/what-is-cloud-run - Google Cloud, “Functions best practices”, consultado el 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/tips/functions-best-practices?hl=en - Node.js, “Process”, consultado el 12/08/2026. URL:
https://nodejs.org/api/process.html - Google Cloud, “Graceful shutdowns on Cloud Run: Deep dive”, consultado el 12/08/2026. URL:
https://cloud.google.com/blog/topics/developers-practitioners/graceful-shutdowns-cloud-run-deep-dive