tutorial

Monitoring Your Beaver Habits Instance with Vigilmon

Keep your self-hosted habit tracker healthy with uptime checks, API endpoint monitoring, database connectivity, streak calculation, and heartbeat monitoring for the daily reminder scheduler.

Monitoring Your Beaver Habits Instance with Vigilmon

Beaver Habits is a self-hosted habit tracker and journal application — track daily habits, build streaks, add notes, and visualize your progress over time. It runs on Python/FastAPI with a Vue.js frontend, typically on port 8080.

The app is deceptively critical infrastructure for its users: streaks break if the reminder scheduler stops firing, and habit completion data is the kind of thing people revisit months later. A silent database failure or a crashed API can mean lost records and broken streaks that can't be reconstructed.

This tutorial sets up production monitoring for Beaver Habits using Vigilmon:

  • Web server and API availability
  • Database connectivity (PostgreSQL or SQLite)
  • Habit API endpoint response times
  • Streak calculation service health
  • Authentication service monitoring
  • Heartbeat monitoring for the daily reminder scheduler
  • Data export health
  • TLS certificate expiry
  • Slack alerting

Step 1: Add a health check endpoint

Beaver Habits runs on FastAPI, which makes adding a health endpoint straightforward.

# app/routes/health.py
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from app.database import get_db_session
from sqlalchemy import text
import os

router = APIRouter()

@router.get("/health")
async def health_check():
    checks = {}

    # Database connectivity
    try:
        async with get_db_session() as session:
            await session.execute(text("SELECT 1"))
        checks["database"] = "ok"
    except Exception as e:
        checks["database"] = f"error: {str(e)}"

    # Check that habit data directory exists (for SQLite setups)
    data_dir = os.getenv("DATA_DIR", "/data")
    checks["storage"] = "ok" if os.path.isdir(data_dir) else "error"

    all_ok = all(v == "ok" for v in checks.values())

    return JSONResponse(
        content={"status": "ok" if all_ok else "degraded", "checks": checks},
        status_code=200 if all_ok else 503,
    )

Register the router in your main app:

# app/main.py
from app.routes.health import router as health_router

app.include_router(health_router)

Test it:

curl http://localhost:8080/health
# {"status":"ok","checks":{"database":"ok","storage":"ok"}}

Step 2: Monitor web server availability

Point Vigilmon at your health endpoint:

  1. Sign up at vigilmon.online
  2. Click New Monitor → HTTP
  3. URL: https://habits.yourdomain.com/health
  4. Check interval: 1 minute
  5. Expected status: 200
  6. Save

This catches the most common failure mode: the Python process crashes or the container stops, making the service completely unavailable.


Step 3: Monitor the habits API endpoint

The /api/habits endpoint is the core read path — it powers the main habit list and dashboard.

In Vigilmon, create an HTTP monitor:

  • URL: https://habits.yourdomain.com/api/habits
  • Expected status: 200 or 401 (depending on whether the endpoint requires auth for listing)
  • Response time alert: 2000ms (habits list should be fast; a slow response indicates a database query problem)

If the endpoint requires authentication, monitor the response status from an unauthenticated request:

  • Expected status: 401 — a 401 means the server processed the request and auth middleware responded. Any 502, 503, or timeout indicates a real failure.

Step 4: Monitor the habit completion recording endpoint

Habit completion recording (/api/records) is the most write-sensitive endpoint. Users complete habits on their phone and expect the data to be saved. Monitor it with a HEAD or OPTIONS request:

  • URL: https://habits.yourdomain.com/api/records
  • Method: OPTIONS
  • Expected status: 200 or 405

For a deeper functional check, add a synthetic probe:

#!/bin/bash
# probe-habit-record.sh
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
  -X POST https://habits.yourdomain.com/api/records \
  -H "Authorization: Bearer $PROBE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"habit_id":"probe-habit","date":"2000-01-01","completed":false}')

# Expect 200 (created), 409 (already exists), or 404 (probe habit not found) — all indicate API is alive
if [[ "$RESPONSE" =~ ^(200|409|404)$ ]]; then
  curl -s "$VIGILMON_RECORDS_HEARTBEAT_URL" > /dev/null
fi

Step 5: Database connectivity monitoring

Database failures are the most common cause of data loss in self-hosted apps. The health check in Step 1 already tests database connectivity, so your Vigilmon HTTP monitor on /health covers this.

For deeper database monitoring — particularly if you're running PostgreSQL — add a direct TCP port monitor:

In Vigilmon:

  1. Click New Monitor → TCP
  2. Host: localhost (or your DB host)
  3. Port: 5432 (PostgreSQL) or leave unconfigured for SQLite (file-based)
  4. Save

A TCP monitor catches cases where PostgreSQL is unreachable even before application-level errors surface.


Step 6: Streak calculation service health

Streak calculation is typically computed on read from the completion records. If the streak calculation is producing wrong results, it won't surface as an HTTP error — users will see incorrect streak counts.

Monitor streak correctness with a functional probe:

#!/usr/bin/env python3
# probe-streak-calculation.py
import requests
import os
import sys

base_url = os.environ["BEAVER_HABITS_URL"]
token = os.environ["PROBE_TOKEN"]
heartbeat_url = os.environ["VIGILMON_STREAK_HEARTBEAT_URL"]

headers = {"Authorization": f"Bearer {token}"}

# Fetch habits and verify streak field is present and numeric
response = requests.get(f"{base_url}/api/habits", headers=headers, timeout=10)
if response.status_code != 200:
    sys.exit(1)

habits = response.json()
for habit in habits[:3]:  # spot-check first 3 habits
    if "streak" not in habit or not isinstance(habit["streak"], int):
        sys.exit(1)

# Ping heartbeat — streak calculation is working
requests.get(heartbeat_url, timeout=5)

Schedule this script every 30 minutes and pair it with a Vigilmon heartbeat monitor.


Step 7: Heartbeat monitoring for the daily reminder scheduler

Daily reminders are a core Beaver Habits feature — they're what keeps users coming back to complete their habits. If the reminder scheduler stops, users silently stop getting notifications and streaks break.

Add a heartbeat ping to your reminder job. If you're using APScheduler inside the app:

# app/scheduler.py
import httpx
import os
import logging

logger = logging.getLogger(__name__)

VIGILMON_REMINDER_HEARTBEAT_URL = os.getenv("VIGILMON_REMINDER_HEARTBEAT_URL")

async def send_daily_reminders():
    """Send daily habit reminders to all users."""
    try:
        # Your existing reminder logic here
        await _dispatch_reminders()

        # Ping heartbeat on success
        if VIGILMON_REMINDER_HEARTBEAT_URL:
            async with httpx.AsyncClient() as client:
                await client.get(VIGILMON_REMINDER_HEARTBEAT_URL, timeout=5)
    except Exception as e:
        logger.error(f"Daily reminder dispatch failed: {e}")
        # No ping → Vigilmon alerts after the window expires

In Vigilmon:

  1. Click New Monitor → Heartbeat
  2. Name: Beaver Habits Daily Reminder Scheduler
  3. Expected interval: 24 hours
  4. Grace period: 30 minutes
  5. Copy the ping URL → set as VIGILMON_REMINDER_HEARTBEAT_URL env variable
  6. Save

If the scheduler crashes, users will miss their reminders but won't know why — this heartbeat makes that failure visible to you before it affects them.


Step 8: User authentication service monitoring

JWT-based authentication is the gatekeeper for all user data. Monitor it specifically:

  • URL: https://habits.yourdomain.com/api/auth/login
  • Method: POST
  • Body: {"username":"probe@example.com","password":"wrongpassword"}
  • Expected status: 401

A 401 confirms the auth endpoint is alive and processing requests. Any 500, 502, or timeout indicates the authentication layer has failed.


Step 9: Data export service health

Habit history export (CSV/JSON) is the user's backup mechanism. If it fails silently, users discover the problem only when they need their data. Monitor the export endpoint:

  • URL: https://habits.yourdomain.com/api/export
  • Method: HEAD
  • Expected status: 200 or 401

For a functional check, run a weekly export probe:

#!/bin/bash
# probe-export.sh
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $PROBE_TOKEN" \
  https://habits.yourdomain.com/api/export?format=json)

if [ "$RESPONSE" = "200" ]; then
  curl -s "$VIGILMON_EXPORT_HEARTBEAT_URL" > /dev/null
fi

Step 10: TLS certificate expiry monitoring

In Vigilmon:

  1. Go to New Monitor → SSL Certificate
  2. Domain: habits.yourdomain.com
  3. Alert thresholds: 30 days (warning) and 7 days (critical)
  4. Save

Habit trackers are often accessed from mobile apps. A TLS error on mobile will show an opaque "Can't connect" message, and users will assume the app is broken rather than suspecting a certificate issue.


Step 11: Slack alerts

In Vigilmon, go to Notifications → New Channel → Slack and paste your Slack incoming webhook URL.

Enable Slack on all Beaver Habits monitors. Critical alert patterns to watch for:

🔴 DOWN: habits.yourdomain.com/health
Status: 503 Service Unavailable
Detected: 5 minutes ago

🔴 MISSED HEARTBEAT: Beaver Habits Daily Reminder Scheduler
Last ping: 26h 10m ago (expected every 24h)

⚠️ SLOW: habits.yourdomain.com/api/habits
Response time: 3,421ms (threshold: 2,000ms)

The heartbeat miss for the reminder scheduler is the most important — it's the alert that saves your users' streaks.


What you've built

| What | How | |------|-----| | Web server health | Vigilmon HTTP monitor → /health | | Database connectivity | Health endpoint DB check + TCP monitor on port 5432 | | Habits API response time | HTTP monitor with 2s alert threshold | | Habit completion API | Synthetic probe + heartbeat | | Streak calculation | Functional probe every 30 minutes | | Daily reminder scheduler | Heartbeat monitor, 24-hour expected interval | | Authentication service | HTTP monitor → /api/auth/login expecting 401 | | Data export | Probe script + weekly heartbeat | | TLS certificate | SSL monitor, 30-day + 7-day alert | | Slack alerts | Vigilmon Slack notification channel |

Your habit tracker keeps your users accountable. Vigilmon keeps your habit tracker accountable.


Next steps

  • Add response time history monitoring to catch slow habit queries before they cause timeout errors
  • Set up a Vigilmon status page so users know where to check during outages
  • If you run Beaver Habits in Docker, add a container health check that feeds into your Vigilmon heartbeat
  • Monitor disk usage if you're storing habit data on an SQLite file — file growth can be slow and silent

Get started 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 →