DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Your Rust Web Services with Vigilmon (Actix-web)

Rust web services built with Actix-web are known for their exceptional performance and memory safety. But even a correctly written Rust service can go down due to infrastructure issues, configuration errors, or upstream dependency failures. External uptime monitoring is the safety net that catches these failures before your users do.

This guide shows you how to add a health check endpoint to an Actix-web service and monitor it with Vigilmon.

Why Rust Services Need Uptime Monitoring

Rust's type system eliminates entire classes of bugs, but it doesn't prevent:

  • Process crashes: Panics, OOM kills, or infrastructure-level crashes
  • Network failures: Load balancer misconfiguration, firewall changes
  • Dependency failures: PostgreSQL, Redis, or external APIs going down
  • Deployment rollout issues: New binary crashes on startup due to env var mismatches

Vigilmon monitors from outside your infrastructure, giving you an independent view of availability.

Step 1: Add a Health Check Endpoint

Basic Health Check

Add a /health route to your Actix-web app:

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

#[get("/health")]
async fn health() -> impl Responder {
    HttpResponse::Ok().json(json!({
        "status": "ok",
        "service": "my-api",
    }))
}

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

Health Check with Database Verification

For services backed by PostgreSQL (via sqlx):

use actix_web::{get, web, HttpResponse, Responder};
use sqlx::PgPool;
use serde_json::json;

#[get("/health")]
async fn health(pool: web::Data<PgPool>) -> impl Responder {
    match sqlx::query("SELECT 1")
        .execute(pool.get_ref())
        .await
    {
        Ok(_) => HttpResponse::Ok().json(json!({
            "status": "ok",
            "db": "connected"
        })),
        Err(e) => HttpResponse::ServiceUnavailable().json(json!({
            "status": "error",
            "db": e.to_string()
        })),
    }
}
Enter fullscreen mode Exit fullscreen mode

Register the pool in your app state:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let pool = PgPool::connect(&std::env::var("DATABASE_URL").unwrap())
        .await
        .expect("Failed to connect to database");

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .service(health)
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}
Enter fullscreen mode Exit fullscreen mode

Health Check with Redis (deadpool-redis)

use deadpool_redis::{Config, Pool, Runtime};
use actix_web::{get, web, HttpResponse, Responder};

#[get("/health")]
async fn health(pool: web::Data<Pool>) -> impl Responder {
    let mut conn = match pool.get().await {
        Ok(c) => c,
        Err(e) => {
            return HttpResponse::ServiceUnavailable().json(
                serde_json::json!({"status": "error", "redis": e.to_string()})
            );
        }
    };

    match deadpool_redis::redis::cmd("PING")
        .query_async::<String>(&mut conn)
        .await
    {
        Ok(_) => HttpResponse::Ok().json(serde_json::json!({"status": "ok", "redis": "connected"})),
        Err(e) => HttpResponse::ServiceUnavailable().json(
            serde_json::json!({"status": "error", "redis": e.to_string()})
        ),
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Build and Test

cargo build --release
./target/release/your-service &

# Test the health endpoint
curl http://localhost:8080/health
# {"status":"ok","db":"connected"}
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure Vigilmon

  1. Go to vigilmon.online and sign up
  2. Click New Monitor
  3. Set:
    • URL: https://api.yourservice.com/health
    • Type: HTTP
    • Interval: 60 seconds
    • Expected status: 200
  4. Add alert channels
  5. Save

Vigilmon will alert you within 60 seconds of any failure.

Deployment Patterns

Docker

FROM rust:1.79-slim AS builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
COPY --from=builder /app/target/release/your-service /usr/local/bin/
EXPOSE 8080
CMD ["your-service"]
Enter fullscreen mode Exit fullscreen mode

Add a Docker health check:

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1
Enter fullscreen mode Exit fullscreen mode

Docker's health check restarts the container if it fails. Vigilmon tells your team.

systemd Service

[Unit]
Description=Actix-web API Service
After=network.target

[Service]
ExecStart=/usr/local/bin/your-service
Restart=always
RestartSec=5
EnvironmentFile=/etc/your-service/env

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Kubernetes

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 30
readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
Enter fullscreen mode Exit fullscreen mode

K8s probes handle internal restarts; Vigilmon provides external visibility and user-facing alerting.

Response Time Monitoring

Rust/Actix-web services are typically fast (<10ms for simple endpoints). If your /health response time exceeds 100ms, something is wrong. Set a Vigilmon response time alert:

  • Alert threshold: 500ms
  • Action: Slack notification + email

Summary

Your Actix-web service is fast, but it's not immortal. Set up external monitoring in 3 steps:

  1. Add a /health endpoint that checks your dependencies
  2. Configure Vigilmon to poll it every 60 seconds
  3. Set up Slack/email alerts for immediate notification

Monitor your Rust service with Vigilmon →

Top comments (0)