Course contentsShow
← Rust
Lesson 1665 of 1,91014. System Integration and EcosystemFree lesson

Systemd Service Integration

You will design your Rust application to interact properly with systemd lifecycle events.

In this lesson, you will learn how to design your Rust application to synchronize its lifecycle with systemd, ensuring clean startup transitions and graceful shutdowns.

Understanding the Lifecycle Contract

Systemd expects a "readiness" signal from a service before it marks it as active. If your application performs heavy initialization—like connecting to a database or warming up a cache—systemd might consider the service "started" before it is actually functional. Conversely, when systemd requests a stop, it expects your process to exit promptly after finishing pending tasks.

To achieve this, we use the sd-notify protocol. Instead of manually parsing the protocol over a socket, we rely on crates that encapsulate these primitives to communicate directly with the systemd manager via NOTIFY_SOCKET.

Implementing Readiness Signaling

Your application should notify systemd as soon as it is prepared to accept traffic. Here is how you structure that lifecycle event:

use sd_notify::NotifyState;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Perform internal setup
    let _ = initialize_heavy_resources()?;

    // 2. Signal readiness to systemd
    if sd_notify::booted()? {
        sd_notify::notify(false, &[NotifyState::Ready])?;
    }

    // 3. Enter the main event loop
    run_service_loop()?;

    // 4. Signal stopping before final exit
    let _ = sd_notify::notify(false, &[NotifyState::Stopping]);
    Ok(())
}

Graceful Shutdown Integration

Systemd sends a SIGTERM signal when it wants to stop your service. Because you have already mastered Signal Handling (Lesson 20), you know that your application must catch this signal to initiate a clean shutdown.

When your signal handler triggers, you should stop accepting new requests, flush your state, and finalize any open database transactions before dropping your handles. By explicitly calling NotifyState::Stopping just before your process exits, you provide systemd with precise telemetry, preventing the service manager from prematurely invoking a SIGKILL timeout. This approach turns your Rust binary into a "good citizen" of the Linux ecosystem, allowing systemd to manage your service dependency graph with perfect accuracy.

Key Takeaway: Use the sd-notify protocol to explicitly synchronize your application's internal state machine with systemd's lifecycle expectations for deterministic startup and controlled shutdowns.