tutorial

How to Monitor Unpoly with Vigilmon

Add uptime monitoring, heartbeat checks, and instant alerts to your Unpoly server-rendered web app with Vigilmon.

Unpoly is a progressive enhancement framework that gives server-rendered HTML apps the speed and feel of a modern SPA — without writing a frontend framework from scratch. Turbolinks-style fast navigation, inline fragment updates, form handling, modals, and more — all from your existing backend. But fast navigation only matters when the backend stays up. Vigilmon gives you the monitoring layer your Unpoly app needs: HTTP uptime checks, heartbeat monitors for background workers, and instant alerts when something breaks.

What You'll Build

  • A /health endpoint for your server-rendered backend (Rails, Django, Laravel, or any HTTP backend)
  • Vigilmon HTTP monitors for the app and the health endpoint
  • A heartbeat monitor for background workers
  • Alert channels (email and Slack)
  • An uptime badge in your server-rendered layout

Prerequisites

  • An Unpoly-powered web app (any backend framework)
  • A free Vigilmon account

Step 1: Add a Health Endpoint to Your Backend

Ruby on Rails

# config/routes.rb
get "/health", to: "health#show"

# app/controllers/health_controller.rb
class HealthController < ApplicationController
  skip_before_action :authenticate_user!, raise: false

  def show
    checks = {}
    degraded = false

    # Database check
    begin
      ActiveRecord::Base.connection.execute("SELECT 1")
      checks[:database] = "ok"
    rescue => e
      checks[:database] = "error: #{e.message}"
      degraded = true
    end

    # Redis / Action Cable check
    begin
      Redis.current.ping
      checks[:redis] = "ok"
    rescue => e
      checks[:redis] = "error: #{e.message}"
      degraded = true
    end

    render json: {
      status: degraded ? "degraded" : "ok",
      timestamp: Time.current.iso8601,
      checks: checks
    }, status: degraded ? :service_unavailable : :ok
  end
end

Django (Python)

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("health/", views.health_check, name="health"),
]

# views.py
import json
from django.http import JsonResponse
from django.db import connection

def health_check(request):
    checks = {}
    degraded = False

    # Database check
    try:
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        checks["database"] = "ok"
    except Exception as e:
        checks["database"] = f"error: {e}"
        degraded = True

    return JsonResponse(
        {"status": "degraded" if degraded else "ok", "checks": checks},
        status=503 if degraded else 200,
    )

Laravel (PHP)

<?php
// routes/web.php
Route::get('/health', function () {
    $checks = [];
    $degraded = false;

    try {
        DB::select('SELECT 1');
        $checks['database'] = 'ok';
    } catch (\Exception $e) {
        $checks['database'] = 'error: ' . $e->getMessage();
        $degraded = true;
    }

    return response()->json([
        'status'    => $degraded ? 'degraded' : 'ok',
        'timestamp' => now()->toIso8601String(),
        'checks'    => $checks,
    ], $degraded ? 503 : 200);
});

Test it:

curl http://localhost:3000/health | jq .
# {"status":"ok","timestamp":"2026-07-03T...","checks":{"database":"ok","redis":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor.

Monitor 1: App Root

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | A stable string from your layout HTML (e.g. app name or logo alt text) | | Check interval | 1 minute |

Monitor 2: Health Endpoint

| Field | Value | |---|---| | URL | https://yourapp.com/health | | Method | GET | | Expected status | 200 | | Expected body | "status":"ok" | | Check interval | 1 minute |

Two monitors, two failure domains: the web server layer and the database/dependency layer.


Step 3: Heartbeat for Background Workers

Unpoly apps commonly pair with Sidekiq (Rails), Celery (Django), or Laravel Horizon (PHP) for background processing. A Vigilmon heartbeat catches silent worker failures.

Sidekiq (Rails)

# app/jobs/sync_job.rb
class SyncJob < ApplicationJob
  queue_as :default

  def perform
    # Your actual work
    DataSyncService.run!

    # Ping heartbeat only on success
    heartbeat_url = ENV["VIGILMON_HEARTBEAT_URL"]
    return unless heartbeat_url.present?

    Net::HTTP.get(URI(heartbeat_url))
  rescue => e
    Rails.logger.error "[SyncJob] Failed: #{e.message}"
    # No ping → Vigilmon alerts after the grace window
    raise
  end
end

Set up the heartbeat in Vigilmon:

  1. Monitors → New Heartbeat Monitor
  2. Expected interval: set to 2× your job schedule (e.g. 30 minutes if job runs every 15 min)
  3. Copy the ping URL and add to your environment:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx

Step 4: Add an Uptime Badge to Your Layout

Since Unpoly serves traditional HTML, drop the badge directly into your server-rendered layout partial:

<!-- Rails: app/views/layouts/_footer.html.erb -->
<!-- Django: templates/partials/footer.html -->
<!-- Laravel: resources/views/partials/footer.blade.php -->

<footer class="app-footer">
  <a href="https://vigilmon.online/status/your-monitor-slug"
     target="_blank"
     rel="noopener noreferrer"
     aria-label="Service uptime status">
    <img src="https://vigilmon.online/api/badge/your-monitor-id.svg"
         alt="Uptime"
         width="120"
         height="20">
  </a>
</footer>

Because Unpoly preserves HTML across navigations (via up-hungry or layout persistence), the badge appears on every page without additional JavaScript.


Step 5: Set Up Alert Channels

In Vigilmon → Alert Channels:

Email

  1. Alert Channels → Email → add your on-call address
  2. Attach to both HTTP monitors and the heartbeat

Slack

  1. Create a Slack Incoming Webhook for #alerts
  2. Alert Channels → Webhook → paste the URL
  3. Payload template:
{
  "text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}

Step 6: Verify the Setup

# 1. Health endpoint returns 200
curl -s https://yourapp.com/health | jq .

# 2. Disable the database connection → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes

# 3. Stop the background worker process
# Expected: heartbeat alert fires after the grace window

# 4. Vigilmon → "Test Alert" → verify delivery to email/Slack

Summary

| Monitor | What It Catches | |---|---| | HTTP root check | Web server, Nginx/Puma/Gunicorn failures | | HTTP health endpoint | Database, Redis, and dependency failures | | Heartbeat | Silent background worker failures |


Next Steps

  • Set up separate monitors for staging and production environments
  • Use Vigilmon's response time graphs to identify slow database queries correlated with Unpoly fragment fetch times
  • Add PagerDuty integration for on-call escalation beyond Slack

Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.

Monitor your app with Vigilmon

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

Start free →