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
/healthendpoint 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 Vigilmon → Monitors → 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:
- Monitors → New Heartbeat Monitor
- Expected interval: set to 2× your job schedule (e.g. 30 minutes if job runs every 15 min)
- 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:
- Alert Channels → Email → add your on-call address
- Attach to both HTTP monitors and the heartbeat
Slack
- Create a Slack Incoming Webhook for
#alerts - Alert Channels → Webhook → paste the URL
- 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.