Skip to content

rust.tokio.missing_graceful_shutdown

Stability High

Detects Tokio applications without graceful shutdown handling.

Without graceful shutdown:

  • Data loss — In-progress requests terminated mid-operation
  • Broken connections — Clients see connection reset errors
  • Database corruption — Transactions interrupted
  • Deploy failures — Kubernetes kills pods that don’t stop cleanly

Graceful shutdown lets existing work complete before the process exits.

// ❌ Before
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(handler));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}

SIGTERM kills the process immediately.

// ✅ After
use tokio::signal;
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(handler));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
  • Tokio main without signal handling
  • Server without with_graceful_shutdown
  • Missing SIGTERM handler

Unfault adds graceful shutdown signal handling.

// Set a shutdown timeout
let server = axum::Server::bind(&addr)
.serve(app.into_make_service())
.with_graceful_shutdown(shutdown_signal());
// Force shutdown after timeout
tokio::select! {
_ = server => {},
_ = tokio::time::sleep(Duration::from_secs(30)) => {
tracing::warn!("graceful shutdown timed out, forcing exit");
}
}
// Cleanup resources
cleanup_connections().await;
flush_metrics().await;