DEV Community

Vigilmon
Vigilmon

Posted on

Uptime Monitoring for Rust Web Apps (Axum, Actix, Warp) with Vigilmon

Rust web applications — whether built with Axum, Actix-web, or Warp — are known for performance and reliability. But even a perfectly written Rust service needs external uptime monitoring. Memory safety doesn't protect against deployment failures, misconfigurations, or infrastructure outages.

This guide shows how to add health check endpoints to Rust web apps and monitor them with Vigilmon.

Why Rust Apps Need External Monitoring

Rust's compile-time guarantees eliminate many runtime bugs, but they don't prevent:

  • Container crashes due to OOM kills
  • Failed deployments that leave no instance running
  • Network issues between your app and its dependencies
  • Infrastructure outages (cloud provider, CDN, DNS)
  • SSL certificate expiry

External monitoring checks your endpoint from outside your infrastructure, giving you an independent view of what users experience.

Adding a Health Endpoint to Axum

Axum is the most popular modern Rust web framework. Adding a health endpoint is straightforward:

use axum::{
    routing::get,
    Router,
    Json,
};
use serde_json::{json, Value};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/health", get(health_handler))
        .route("/", get(root_handler));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn health_handler() -> Json<Value> {
    Json(json!({ "status": "ok" }))
}
Enter fullscreen mode Exit fullscreen mode

For a more detailed health check that includes dependency status:

use axum::{http::StatusCode, Json};
use serde_json::{json, Value};

async fn health_handler(
    State(db): State<sqlx::PgPool>,
) -> Result<Json<Value>, StatusCode> {
    // Check database connectivity
    sqlx::query("SELECT 1")
        .execute(&db)
        .await
        .map_err(|_| StatusCode::SERVICE_UNAVAILABLE)?;

    Ok(Json(json!({
        "status": "ok",
        "db": "ok"
    })))
}
Enter fullscreen mode Exit fullscreen mode

This returns 200 when the database is reachable and 503 when it's not — Vigilmon will alert on the 503.

Adding a Health Endpoint to Actix-web

use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde_json::json;

async fn health() -> impl Responder {
    HttpResponse::Ok().json(json!({ "status": "ok" }))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/health", web::get().to(health))
    })
    .bind("0.0.0.0:8080")?
    .run()
    .await
}
Enter fullscreen mode Exit fullscreen mode

Adding a Health Endpoint to Warp

use warp::Filter;
use serde_json::json;

#[tokio::main]
async fn main() {
    let health = warp::path("health")
        .and(warp::get())
        .map(|| warp::reply::json(&json!({ "status": "ok" })));

    warp::serve(health)
        .run(([0, 0, 0, 0], 3030))
        .await;
}
Enter fullscreen mode Exit fullscreen mode

Setting Up Vigilmon

  1. Sign up at vigilmon.online — free tier includes 3 monitors
  2. Click Add Monitor
  3. Configure:
    • URL: https://yourdomain.com/health
    • Interval: 1 minute
    • Regions: 2–3 external check regions
    • Expected status: 200
    • Timeout: 10 seconds (Rust services typically respond in milliseconds)
    • Alert after: 2 consecutive failures
  4. Add an alert channel (email, Slack, PagerDuty webhook)
  5. Save

Rust-Specific Monitoring Considerations

Panic handling: If your Rust service panics, it typically crashes the thread or process. Tokio-based servers catch panics per task, but a bad panic in the accept loop or shutdown path can kill the whole server. Monitor at 1-minute intervals to catch these quickly.

Async runtime exhaustion: If your async runtime is starved (blocking code on an async thread, or too many concurrent tasks), the health endpoint may time out even though the process is still running. A 10-second timeout will catch this.

Binary deployment: Rust binaries are self-contained, which means a failed deployment often means "the old binary is gone, the new one failed to start." External monitoring catches this immediately.

Memory usage: Rust programs don't have a GC, so memory leaks are rare but not impossible. Monitoring response times over time can hint at degrading performance before a full restart.

Health Check Best Practices for Rust

use axum::{http::StatusCode, Json, extract::State};
use serde::Serialize;

#[derive(Serialize)]
struct HealthResponse {
    status: &'static str,
    version: &'static str,
}

async fn health_handler() -> (StatusCode, Json<HealthResponse>) {
    (
        StatusCode::OK,
        Json(HealthResponse {
            status: "ok",
            version: env!("CARGO_PKG_VERSION"),
        }),
    )
}
Enter fullscreen mode Exit fullscreen mode

Including the version in the health response makes it easy to verify which version is deployed when investigating incidents.

Alerting Configuration

For a production Rust service:

Setting Recommended Why
Interval 1 minute Rust services are fast; 1-min catches failures quickly
Timeout 10 seconds If a Rust service takes >10s, something is seriously wrong
Alert after 2 failures One failure may be a brief deployment
SSL check Enabled Catch cert expiry before it breaks everything
Regions 2–3 Cross-check from independent locations

Status Page for Users

If your Rust service is part of a public API, set up a Vigilmon status page:

  1. Go to Status Pages in Vigilmon
  2. Create a page and add your monitors
  3. Share the URL in your documentation

Get Started

Rust gives you performance and memory safety. Add Vigilmon for the uptime monitoring layer — so you know immediately when your perfectly safe Rust service is down.

Free tier: 3 monitors, 1-minute checks, no credit card required.

Top comments (0)