tutorial

Monitoring Fermyon Spin Applications with Vigilmon: Health Checks, Triggers & Alerting

Production-grade uptime monitoring for Fermyon Spin WebAssembly apps — health endpoints, trigger heartbeats, Spin component checks, and Vigilmon HTTP monitors.

Fermyon Spin is the fastest way to build and deploy WebAssembly microservices. Spin apps start in milliseconds, run in an isolated Wasm sandbox, and can be hosted on Fermyon Cloud or self-hosted with Spin's built-in server. But the same isolation that makes Wasm safe also makes failures invisible: a component that panics returns a 500, a misconfigured key-value store silently drops writes, and a Redis trigger that falls behind never surfaces in your logs. Vigilmon provides the external vantage point that catches these issues before your users do.

This tutorial shows how to add production-grade monitoring to a Fermyon Spin application.

What You'll Build

  • A /health route in your Spin component that checks real dependencies
  • A Vigilmon HTTP monitor targeting your Spin app's health endpoint
  • Heartbeat monitoring for scheduled Spin triggers
  • Alert channels (email + webhook) configured in Vigilmon

Prerequisites

  • Spin CLI v2+ installed and a working Spin app (spin new or existing project)
  • A free account at vigilmon.online
  • (Optional) A key-value store or Redis binding in your spin.toml

Step 1: Add a Health Handler to Your Spin Component

Spin routes HTTP requests to components via spin.toml. Add a dedicated /health route and a corresponding handler that exercises your real dependencies.

spin.toml

[[component]]
id = "api"
source = "target/wasm32-wasi/release/api.wasm"

[component.trigger]
route = "/..."

[component.key_value_stores]
default = { label = "default" }

Rust health handler

use anyhow::Result;
use spin_sdk::{
    http::{IntoResponse, Request, Response},
    http_component,
    key_value::Store,
};

#[http_component]
fn handle_request(req: Request) -> Result<impl IntoResponse> {
    if req.uri().path() == "/health" {
        return handle_health();
    }
    // ... rest of routing
    Ok(Response::builder()
        .status(404)
        .body("Not found")
        .build())
}

fn handle_health() -> Result<impl IntoResponse> {
    let mut checks = std::collections::HashMap::new();
    let mut ok = true;

    // Key-value store probe
    match Store::open_default() {
        Ok(store) => {
            match store.set("__health_probe__", b"1") {
                Ok(_) => match store.get("__health_probe__") {
                    Ok(Some(val)) if val == b"1" => {
                        checks.insert("kv", "ok");
                    }
                    _ => {
                        checks.insert("kv", "read_mismatch");
                        ok = false;
                    }
                },
                Err(e) => {
                    checks.insert("kv", "write_error");
                    ok = false;
                }
            }
        }
        Err(_) => {
            checks.insert("kv", "open_error");
            ok = false;
        }
    }

    let status = if ok { "ok" } else { "degraded" };
    let body = serde_json::json!({ "status": status, "checks": checks });

    Ok(Response::builder()
        .status(if ok { 200 } else { 503 })
        .header("content-type", "application/json")
        .body(body.to_string())
        .build())
}

Build and run locally to verify:

spin build
spin up
curl http://localhost:3000/health
# {"status":"ok","checks":{"kv":"ok"}}

Step 2: Add a Vigilmon HTTP Monitor

  1. Log in to Vigilmon and click Add Monitor → HTTP.
  2. Set URL to https://<your-spin-app>.fermyon.app/health (or your self-hosted domain).
  3. Set Check interval to 60 seconds.
  4. Set Expected status code to 200.
  5. Under Advanced, enable JSON body assertion:
    • Path: status
    • Expected value: ok
  6. Save. Vigilmon will alert you whenever the health check fails or returns degraded.

Alert channels

Go to Alerts → Add channel:

  • Email — your on-call address for immediate notifications
  • Webhook — Slack incoming webhook or PagerDuty endpoint

Step 3: Heartbeat Monitoring for Spin Triggers

Spin supports Redis and HTTP triggers for event-driven components. If your app uses a Redis trigger to process messages, a silent consumer stall won't appear in HTTP logs. Use Vigilmon heartbeats to detect it.

At the end of each successful trigger invocation, ping Vigilmon's heartbeat URL:

use spin_sdk::outbound_http;

fn notify_vigilmon_heartbeat() -> Result<()> {
    let req = http::Request::builder()
        .method("POST")
        .uri("https://vigilmon.online/api/heartbeat/<your-heartbeat-id>")
        .body(None)?;

    let _ = outbound_http::send_request(req)?;
    Ok(())
}

Call notify_vigilmon_heartbeat() at the end of your trigger handler (only on success — on failure, do not ping so Vigilmon fires an alert).

In Vigilmon, create a Heartbeat monitor and set the grace period to 1.5× your expected trigger interval. If the heartbeat goes missing, you get an immediate alert.


Step 4: Monitoring Fermyon Cloud Deployments

When deploying to Fermyon Cloud, your app URL follows the pattern https://<app>-<hash>.fermyon.app. After each spin deploy, verify the health endpoint responds before removing monitoring for the old URL:

spin deploy
NEW_URL=$(spin cloud app info --output json | jq -r '.url')
echo "New URL: $NEW_URL/health"
curl "$NEW_URL/health"

Update your Vigilmon monitor URL to the new deployment URL from the Vigilmon dashboard, or use a custom domain with spin cloud custom-domain add to keep the monitor URL stable across deployments.


Step 5: Key Metrics to Alert On

| Metric | What it signals | Vigilmon check | |---|---|---| | /health returns 5xx | Component panic or crash | HTTP status assertion | | /health returns degraded | KV or upstream dependency failure | JSON body assertion | | Response time > 500 ms | Wasm JIT warm-up or I/O timeout | Response time threshold | | Heartbeat missing | Trigger consumer stalled | Heartbeat grace period | | Repeated 503 from Fermyon Cloud | App resource limit hit | HTTP status + alert |


Step 6: Status Page

Add a public status page so your users can check availability without contacting support. In Vigilmon, go to Status Pages → Create, add your Spin health monitor, and copy the badge:

<img src="https://vigilmon.online/badge/<monitor-id>.svg" alt="API Status" />

Embed this badge in your README or documentation site.


What You Get

| Scenario | How Vigilmon catches it | |---|---| | Wasm component panics | HTTP monitor sees 5xx, fires alert | | Key-value store becomes unavailable | /health returns degraded | | Redis trigger consumer stalls | Heartbeat not received in grace period | | Fermyon Cloud deployment fails | New URL health check fails | | Upstream service outage | Dependency check in /health fails |


Fermyon Spin makes deploying WebAssembly services simple — but simple deploys still need external monitoring. Vigilmon gives you the independent health signal that tells you when something is wrong before your users report it.

Start monitoring your Spin app today — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →