Problem
Standby Actors that serve HTTP requests will drop in-flight requests during shutdown (worker deployments, idle timeouts, manual aborts) unless they implement a specific shutdown pattern. This pattern is not documented anywhere, and our existing examples and templates don't include it.
The required pattern is:
await Actor.init({ gracefulShutdown: false });
// ... server setup ...
Actor.on('aborting', () => {
server.close(async () => {
await Actor.exit();
});
});
Both pieces are mandatory:
-
gracefulShutdown: false — Without this, the SDK terminates the process as soon as the aborting event arrives, before server.close() can drain in-flight requests. The aborting handler starts executing but the process exits before the callback fires. Verified locally: the client receives "socket hang up".
-
Actor.on('aborting', ...) with server.close() — Stops accepting new connections and waits for in-flight requests to complete before exiting.
This is different from regular (non-Standby) Actors, which migrate to a new worker with their state intact. Standby runs are replaced: a new run starts on a fresh worker, then the old run is terminated. Without this pattern, any request being served at the moment of termination is dropped.
Related
- https://github.com/apify/apify-worker/issues/1976 — Worker-side bug where the fallback shutdown path sends a bare SIGTERM without emitting the
aborting event at all. Even after that fix lands, Actors still need gracefulShutdown: false to properly drain connections.
Problem
Standby Actors that serve HTTP requests will drop in-flight requests during shutdown (worker deployments, idle timeouts, manual aborts) unless they implement a specific shutdown pattern. This pattern is not documented anywhere, and our existing examples and templates don't include it.
The required pattern is:
Both pieces are mandatory:
gracefulShutdown: false— Without this, the SDK terminates the process as soon as theabortingevent arrives, beforeserver.close()can drain in-flight requests. Theabortinghandler starts executing but the process exits before the callback fires. Verified locally: the client receives "socket hang up".Actor.on('aborting', ...)withserver.close()— Stops accepting new connections and waits for in-flight requests to complete before exiting.This is different from regular (non-Standby) Actors, which migrate to a new worker with their state intact. Standby runs are replaced: a new run starts on a fresh worker, then the old run is terminated. Without this pattern, any request being served at the moment of termination is dropped.
Related
abortingevent at all. Even after that fix lands, Actors still needgracefulShutdown: falseto properly drain connections.