Skip to content

nanasi-apps/cf-uptime-watcher

Repository files navigation

cf-uptime-watcher

Lightweight uptime monitoring that runs entirely on Cloudflare's free tier — no servers, no subscriptions.

cf-uptime-watcher is a self-hosted uptime monitor built on Cloudflare Workers and D1. It checks your HTTP endpoints every 5 minutes, stores history in a serverless SQLite database, and fires Slack or Discord alerts the moment a service goes down (or recovers).

Features

  • Zero-infrastructure — runs as a Cloudflare Worker with Cron Triggers; no VMs or containers
  • Scheduled checks — polls every 5 minutes out of the box (configurable via cron)
  • Flexible HTTP checks — GET or POST, custom headers, request body, configurable timeout and expected status code
  • Check history — stores response time, HTTP status, and error messages for every check
  • Uptime percentage — computed per-monitor across all stored results
  • Instant alerts — notifies Slack and Discord when a monitor changes state (down → up or up → down)
  • Custom notification templates — use {{monitor.name}}, {{status}}, {{responseTime}}, {{error}}, etc.
  • Bulk import — import monitors from JSON with duplicate-skipping
  • Type-safe REST API — powered by oRPC with an auto-generated OpenAPI spec
  • Web dashboard — built with Nuxt 4 + Vue 3, TailwindCSS, and DaisyUI
  • Password authentication — simple bearer-token auth for write operations

Tech Stack

Layer Technology
Runtime Cloudflare Workers
Framework Nuxt 4 / Vue 3
Database Cloudflare D1 (SQLite)
ORM Drizzle ORM
API oRPC + OpenAPI
Validation Zod
Styling TailwindCSS v4 + DaisyUI
Deployment Wrangler

Getting Started

Prerequisites

1. Clone and install

git clone https://github.com/mattyatea/cf-uptime-watcher.git
cd cf-uptime-watcher
pnpm install

2. Create the D1 database

pnpm wrangler d1 create healthcheck

Copy the database_id from the output and update wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "healthcheck"
database_id = "<your-database-id>"

3. Run database migrations

# Apply to local (for development)
pnpm wrangler d1 migrations apply healthcheck --local

# Apply to remote (production)
pnpm drizzle:migrate

4. Set your admin password

Register AUTH_PASSWORD as a Cloudflare secret so it is never stored in plain text:

pnpm wrangler secret put AUTH_PASSWORD

Wrangler will prompt you to enter the value interactively. The secret is encrypted and injected at runtime — it does not appear in wrangler.toml or source control.

For local development, create a .dev.vars file in the project root (already gitignored):

# .dev.vars
AUTH_PASSWORD=your-local-password

5. Start the development server

pnpm dev

Open http://localhost:3000 to see the dashboard.

Deployment

Build and deploy to Cloudflare Workers in a single command:

pnpm deploy

This runs nuxi build && wrangler deploy and publishes your Worker with the configured Cron Trigger (*/5 * * * *).

Notification Channels

Discord

Create a webhook in your Discord server (Server Settings → Integrations → Webhooks) and add a Discord channel via the dashboard or API.

Slack

Create an Incoming Webhook for your Slack workspace and add it via the dashboard or API.

Custom Templates

Both channel types support a Mustache-style template string. Available variables:

Variable Description
{{monitor.name}} Monitor display name
{{monitor.url}} Monitored URL
{{monitor.method}} HTTP method
{{status}} DOWN or RECOVERED
{{statusCode}} HTTP response code (or N/A)
{{responseTime}} Response time in ms (or N/A)
{{error}} Error message if the check failed
{{timestamp}} ISO 8601 timestamp of the check

Default template:

[{{status}}] {{monitor.name}}
URL: {{monitor.url}}
Status: {{statusCode}} | Response: {{responseTime}}
{{error}}

API

The REST API is fully documented via an auto-generated OpenAPI spec. Once running, visit:

http://localhost:3000/api/openapi.json

Authentication for write operations uses a Bearer token that matches your AUTH_PASSWORD.

Quick reference

Method Path Auth Description
GET /api/monitors List all monitors with status
POST /api/monitors Bearer Create a monitor
PATCH /api/monitors/:id Bearer Update a monitor
DELETE /api/monitors/:id Bearer Delete a monitor
POST /api/monitors/:id/check Bearer Trigger an immediate check
GET /api/monitors/:id/history Get check history
POST /api/monitors/import Bearer Bulk import monitors from JSON
PUT /api/monitors/:id/channels Bearer Assign notification channels
GET /api/notification-channels Bearer List notification channels
POST /api/notification-channels Bearer Create a notification channel
POST /api/notification-channels/:id/test Bearer Send a test alert
POST /api/auth/login Validate admin password

Bulk import example

curl -X POST https://your-worker.workers.dev/api/monitors/import \
  -H "Authorization: Bearer your-password" \
  -H "Content-Type: application/json" \
  -d '{
    "skipDuplicates": true,
    "monitors": [
      { "name": "API", "url": "https://api.example.com/health", "method": "GET" },
      { "name": "App", "url": "https://app.example.com", "method": "GET" }
    ]
  }'

Configuration Reference

wrangler.toml

Key Default Description
triggers.crons */5 * * * * Cron schedule for health checks
d1_databases[].database_id Your Cloudflare D1 database ID

Secrets

Set via pnpm wrangler secret put <KEY>. Never commit these to source control.

Key Description
AUTH_PASSWORD Admin password for authenticated endpoints

Monitor options

Field Type Description
name string Display name (max 100 chars)
url string Target URL
method GET | POST HTTP method
expectedStatus number Expected HTTP status (default 200)
timeout number Request timeout in seconds (1–120)
headers string | null JSON string of request headers
body string | null Request body for POST monitors
active boolean Whether the monitor runs on schedule

Development

# Start dev server
pnpm dev

# Type-check, lint, and format
vp check

# Run linter only
pnpm lint

# Generate Drizzle migrations after schema changes
pnpm drizzle:generate

# Open Drizzle Studio
pnpm drizzle:studio

# Regenerate Cloudflare type bindings
pnpm generate-types

About

A lightweight uptime monitor that can be deployed to Cloudflare

Topics

Resources

License

Stars

Watchers

Forks

Contributors