On SIGTERM or SIGINT, Revali finishes the requests it is already serving before it exits. This is on by default. Tune it when you deploy behind a load balancer or an orchestrator such as Kubernetes.
@App()
final class MainApp extends AppConfig {
MainApp() : super.fromEnv();
@override
Duration get drainDelay => const Duration(seconds: 10);
@override
Duration get shutdownTimeout => const Duration(seconds: 15);
@override
Future<void> onServerStopped() async {
await database.close();
}
}
| Member | Default | Description |
|---|---|---|
drainDelay |
Duration.zero |
How long readiness reports
503
while the server still accepts connections. Applies to
SIGTERM
only.
|
shutdownTimeout |
15 seconds | How long to wait for in-flight requests before giving up and exiting anyway. |
onServerStopped() |
no-op | Runs after requests have drained. Close database pools and file handles here. If it throws, the error is logged and shutdown continues. |
handleShutdownSignals |
true |
Set to
false
to handle signals yourself. The server then stops listening for them, and exiting is up to you.
|
What Happens on SIGTERM#
-
If the app has message consumers, they are paused and drained first, for up to
shutdownTimeout. -
/readyzstarts returning503 {"status":"draining"}, while the server keeps accepting and serving requests fordrainDelay. - The server stops accepting new connections.
- In-flight requests finish, for up to
shutdownTimeout. onServerStopped()runs, and the process exits with code0.
SIGINT (Ctrl-C) follows the same steps but skips drainDelay. A second signal during shutdown is ignored.
drainDelay#
A load balancer doesn't notice a closed socket. It keeps sending traffic until its own readiness check fails. During
drainDelay, readiness already reports 503 but requests are still served, so the load balancer has time to stop sending traffic before the socket closes.
- Leave it at
0when nothing load-balances in front of the server. - Otherwise, set it longer than the probe period multiplied by the failure threshold. Kubernetes defaults to 10s × 3.
-
Keep
drainDelay + shutdownTimeoutunder the platform's kill grace period. Kubernetes sendsSIGKILLafter 30s by default. The consumer drain and the request drain each wait up toshutdownTimeout.
Worker Isolates#
With workers greater than 1, the parent isolate tells every worker to drain at the same time as itself. Every isolate reports
503, and the process exits once all of them have finished. That wait is bounded by drainDelay + shutdownTimeout. Workers never install their own signal handlers.