Skip to content

Graceful shutdown hangs when using TcpListener::from_raw_fd #3560

@rg-atte

Description

@rg-atte
  • I have looked for existing issues (including closed) about this

Bug Report

Version

├── axum v0.8.7
│ ├── axum-core v0.5.5

Platform

Linux 6.14.0-34-generic #34~24.04.1-Ubuntu

Description

When using graceful_shutdown with TcpListener created with from_raw_fd the shutdown hangs until further requests which fail are made. The issue can also be reproduced with a blocking TcpStream created with from_raw_fd instead of TcpListener directly.

Short reproduction of the bug:

use std::{os::fd::FromRawFd, time::Duration};

use axum::{Router, response::IntoResponse};
use tokio::{net::{TcpListener}, time};
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};

async fn timeout() -> () {
    time::sleep(Duration::from_secs(15)).await;
    println!("Graceful shutdown");
}

#[tokio::main]
async fn main() {
    let _log = tracing_subscriber::registry()
        .with(EnvFilter::from_default_env().add_directive("trace".parse().unwrap()))
        .with(
            fmt::layer()
                .with_ansi(true)
                .with_level(true)
                .with_target(true),
        )
        .try_init();

    let listener = unsafe { std::net::TcpListener::from_raw_fd(3) };
    let tokio_listener = TcpListener::from_std(listener).unwrap();
    let request_handler = async |input: String| input.into_response();

    let router = Router::new().fallback(request_handler);
    axum::serve(tokio_listener, router)
        .with_graceful_shutdown(timeout())
        .await
        .unwrap();
}

What happens:

$ systemd-socket-activate -l 127.0.0.1:9123 ./target/release/fd_repro
Listening on 127.0.0.1:9123 as 3.
Communication attempt on fd 3.
Execing ./target/release/fd_repro (./target/release/fd_repro)
2025-11-18T11:38:53.378754Z TRACE axum::serve: connection 127.0.0.1:57154 accepted
Graceful shutdown
2025-11-18T11:39:08.379283Z TRACE axum::serve: received graceful shutdown signal. Telling tasks to shutdown
# Hangs here after the shutdown future completes
# The following log is from trying to make a request until the program manages to shut down
2025-11-18T11:39:13.332484Z TRACE axum::serve: connection 127.0.0.1:44526 accepted
2025-11-18T11:39:13.332559Z TRACE axum::serve: signal received in task, starting graceful shutdown
2025-11-18T11:39:13.332572Z TRACE axum::serve: failed to serve connection: Cancelled
...
2025-11-18T11:40:06.390920Z TRACE axum::serve: connection 127.0.0.1:33176 accepted
2025-11-18T11:40:06.390963Z TRACE axum::serve: signal received, not accepting new connections
2025-11-18T11:40:06.390988Z TRACE axum::serve: waiting for 1 task(s) to finish
2025-11-18T11:40:06.390986Z TRACE axum::serve: signal received in task, starting graceful shutdown
2025-11-18T11:40:06.391006Z TRACE axum::serve: failed to serve connection: Cancelled
# program exits here

What should happen:

systemd-socket-activate -l 127.0.0.1:9123 ./target/release/fd_repro
Listening on 127.0.0.1:9123 as 3.
Communication attempt on fd 3.
Execing ./target/release/fd_repro (./target/release/fd_repro)
2025-11-18T11:43:54.102130Z TRACE axum::serve: connection 127.0.0.1:40936 accepted
Graceful shutdown
2025-11-18T11:44:09.103194Z TRACE axum::serve: received graceful shutdown signal. Telling tasks to shutdown
2025-11-18T11:44:09.103306Z TRACE axum::serve: signal received, not accepting new connections
2025-11-18T11:44:09.103368Z TRACE axum::serve: waiting for 0 task(s) to finish
# Program exits here

Another reproduction I managed to make is replacing the TcpListener from the earlier example with

    let stream = unsafe { std::net::TcpStream::from_raw_fd(3) };
    stream.set_nonblocking(false).unwrap(); // Works as intended when true, hangs when false
    let socket = tokio::net::TcpSocket::from_std_stream(stream);
    socket.set_nodelay(true).unwrap();
    let tokio_listener = socket.listen(1024).unwrap();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions