
UptimeFlare
Run minute-by-minute uptime checks and your own status page on Cloudflare.
UptimeFlare is a self-hosted Better Stack/Pingdom alternative built on Cloudflare (D1, Durable Objects, Pages, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.
Source & license
Upstream license: Apache-2.0
License TL;DR
You can use, change and sell it, including in closed-source products. When sharing copies, include the license, keep required notices and mark changed files. It includes a contributor patent grant with conditions, but no trademark permission or warranty.
Explain Apache 2.0 in plain English →Summary of the main license. Separate packages and assets can have different terms.
Inspect repository ↗Read this project’s actual license ↗Repository owner
See the upstream repository for the original creator and contributors.
Maintain this project? Maintainer verification →Cloudflare hosting
Free tier eligible within limits
A small uptime-monitoring setup can fit Cloudflare Free allowances using Workers, Pages, D1 and SQLite Durable Objects. Eligibility depends on measured CPU, monitor count, status-page traffic and account quotas; it is not a verified zero-cost deployment.
Hosting requirements
- Keep combined Workers and Pages Function requests within the account Free allowance of 100,000 per day and each Worker invocation within 10 ms CPU. Measure history compaction and checks; the upstream claim of 50 monitors is not a tested free-tier capacity here.
- Keep D1 below 5 million rows read/day, 100,000 rows written/day and the account storage allowance. The scheduled Worker writes compacted state on status changes or the configured cooldown; page requests also read it.
- Use the Terraform-created SQLite RemoteChecker class for optional worker:// regional checks and stay within Durable Object request, duration and storage allowances. Other proxy modes and Globalping are separate services with their own terms.
- Use an eligible GitHub Actions plan and provision your own Cloudflare account, token, database and configuration. Private-repository Actions minutes, custom domains and third-party notification delivery can add costs.
- Basic direct checks and the public status page need no paid notification provider. Apprise, hosted notification gateways and external proxies are optional and excluded from this free-tier assessment.
Sources checked 01/10/2026
Repository snapshot: a5670e5. Hosting eligibility reflects the deployment documentation and listed assumptions.
- uptimerobot ↗
Support for HTTP/HTTPS/TCP port monitoring
- better-stack ↗
Support for HTTP/HTTPS/TCP port monitoring
- pingdom ↗
Support for HTTP/HTTPS/TCP port monitoring
- workers ↗
resource "cloudflare_workers_script" "uptimeflare_worker"
- pages ↗
resource "cloudflare_pages_project" "uptimeflare"
- d1 ↗
name = "uptimeflare_d1"
- durable-objects ↗
new_sqlite_classes = ["RemoteChecker"]
- free-tier-eligible ↗
| **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation |
- free-tier-eligible ↗
| Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows |
- free-tier-eligible ↗
| Rows written | 100,000 / day | First 50 million / month included + $1.00 / million rows |
- free-tier-eligible ↗
Workers Free plan can only create and access SQLite-backed Durable Objects.
- free-tier-eligible ↗
migrations = var.enable_do_migration ? { new_tag = "v1" new_sqlite_classes = ["RemoteChecker"] } : null bindings = [{ name = "REMOTE_CHECKER_DO" class_name = "RemoteChecker" type = "durable_object_namespace" }, { name = "UPTIMEFLARE_D1" type = "d1" id = cloudflare_d1_database.uptimeflare_d1.id }] }
- Apache-2.0 ↗
Apache License Version 2.0, January 2004
- architecture ↗
resource "cloudflare_workers_cron_trigger" "uptimeflare_worker_cron" { account_id = var.CLOUDFLARE_ACCOUNT_ID script_name = cloudflare_workers_script.uptimeflare_worker.script_name schedules = [{ cron = "* * * * *" # every 1 minute, you can reduce the write counts by increase the worker settings of `kvWriteCooldownMinutes` }] } resource "cloudflare_pages_project" "uptimeflare" { account_id = var.CLOUDFLARE_ACCOUNT_ID name = "uptimeflare" production_branch = "main" deployment_configs = { # SMH Cloudflare provider will throw an error without preview config preview = { fail_open = false } production = { d1_databases = { UPTIMEFLARE_D1 = { id = cloudflare_d1_database.uptimeflare_d1.id } } compatibility_date = "2025-04-02" compatibility_flags = ["nodejs_compat"] fail_open = false } } # SMH it will error without this build_config build_config = { root_dir = "/" } }
- architecture ↗
resource "cloudflare_workers_script" "uptimeflare_worker" { account_id = var.CLOUDFLARE_ACCOUNT_ID script_name = "uptimeflare_worker" main_module = "worker/dist/index.js" content_file = "worker/dist/index.js" content_sha256 = filesha256("worker/dist/index.js") compatibility_date = "2025-04-02" compatibility_flags = ["nodejs_compat"] observability = { enabled = true logs = { enabled = true invocation_logs = true } } migrations = var.enable_do_migration ? { new_tag = "v1" new_sqlite_classes = ["RemoteChecker"] } : null bindings = [{ name = "REMOTE_CHECKER_DO" class_name = "RemoteChecker" type = "durable_object_namespace" }, { name = "UPTIMEFLARE_D1" type = "d1" id = cloudflare_d1_database.uptimeflare_d1.id }] }
- architecture ↗
export const runtime = 'edge'
- architecture ↗
await getFromStore(process.env as any, 'state')
- architecture ↗
Interactive ping (response time) chart for all types of monitors
Documented public demo screenshot · lyc8503/UptimeFlare repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.
What it can replace
Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.
HTTP/HTTPS/TCP availability checks and a self-hosted status page. Excludes real-user monitoring, logs, traces, synthetic browser journeys, on-call scheduling and complete feature parity.
See supporting source ↗HTTP/HTTPS/TCP availability checks and a self-hosted status page. Excludes real-user monitoring, logs, traces, synthetic browser journeys, on-call scheduling and complete feature parity.
See supporting source ↗HTTP/HTTPS/TCP availability checks and a self-hosted status page. Excludes real-user monitoring, logs, traces, synthetic browser journeys, on-call scheduling and complete feature parity.
See supporting source ↗How it works
The shape of UptimeFlare on Cloudflare, and how it stacks up against the rented tools it replaces.
Architecture
Diagram based on the linked repository documentation. See the sources and hosting assumptions above.
View upstream source ↗Configuration and workflow sources
Reviewed commit a5670e51cbc1. This configuration evidence comes from reading source files. Execution checks, when available, appear in the project's runtime review.
Deployment configuration · 2 files
Cloudflare Workers · compatibility 2025-04-02
uptimeflare · default
UPTIMEFLARE_D1→ D1
Cloudflare Workers · compatibility 2025-04-02
uptimeflare_worker · default
Entrypoint: src/index.ts
UPTIMEFLARE_D1→ D1
Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.
Runtime source · handlers, binding usage and workflow steps
Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.
- L15 · scheduled handler exported · calls getWorkerLocation, console.log, getFromStore, Math.round, Date.now, pLimit, checkQueue.push, limit, doMonitor, Promise.all, state.incidentLen, state.appendIncident, state.getIncident, state.setIncident, formatAndNotify, lastIncident.error.slice, lastIncident.start.push, lastIncident.error.push, state.appendLatency, state.getFirstLatency, state.unshiftLatency, state.shiftIncident, state.unshiftIncident, setToStore, state.getCompactedStateStr
- L5 · isIpAddress calls (conditional paths may differ): hostname.includes, hostname.split, parts.every, test, Number
- L19 · getDomainOnlyIpVersionOption calls (conditional paths may differ): isIpAddress, Number, gpUrl.searchParams.get
- L27 · httpResponseBasicCheck calls (conditional paths may differ): monitor.expectedCodes.includes, JSON.stringify, bodyReader, responseBody.includes, console.log, responseBody.slice
- L73 · getStatusWithGlobalPing calls (conditional paths may differ): getDomainOnlyIpVersionOption, gpUrl.searchParams.get, includes, monitor.method.toUpperCase, Object.fromEntries, map, Object.entries, String, Number, targetUrl.protocol.replace, Date.now, console.log, JSON.stringify, fetchTimeout, measurement.json, json, setTimeout, Math.round, httpResponseBasicCheck, startsWith
- L267 · getStatus calls (conditional paths may differ): Date.now, then, connect, Number, withTimeout, socket.close, console.log, e.message.includes, headers.has, headers.set, fetchTimeout, httpResponseBasicCheck, response.text.bind
- L357 · doMonitor calls (conditional paths may differ): console.log, monitor.checkProxy.startsWith, monitor.checkProxy.replace, env.REMOTE_CHECKER_DO.idFromName, env.REMOTE_CHECKER_DO.get, doStub.getLocationAndStatus, doStub.kill, getStatusWithGlobalPing, json, fetch, JSON.stringify, getStatus
Environment references: env.REMOTE_CHECKER_DO
- L9 · getFromStore calls (conditional paths may differ): env.UPTIMEFLARE_D1.prepare, first, stmt.bind
- L15 · setToStore calls (conditional paths may differ): env.UPTIMEFLARE_D1.prepare, run, stmt.bind
Environment references: env.UPTIMEFLARE_D1
- L14 · handler calls (conditional paths may differ): getFromStore, JSON.stringify, compactedState.getIncident, compactedState.incidentLen, compactedState.getLastLatency
- L4 · getWorkerLocation calls (conditional paths may differ): fetch, res.text, exec
- L24 · withTimeout calls (conditional paths may differ): setTimeout, reject, Promise.race
- L32 · formatStatusChangeNotification calls (conditional paths may differ): Math.round, dateFormatter.format
- L70 · templateWebhookPlayload calls (conditional paths may differ): Object.prototype.hasOwnProperty.call, templateWebhookPlayload
- L82 · webhookNotify calls (conditional paths may differ): Array.isArray, webhookNotify, console.log, JSON.stringify, JSON.parse, templateWebhookPlayload, Object.entries, urlTmp.searchParams.append, v.toString, urlTmp.toString, headers.get, headers.set, toString, Object.fromEntries, headers.entries, fetchTimeout, resp.text
Build and deployment pipeline · 3 GitHub Actions workflows
Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.
Triggers: push, workflow_dispatch
build-and-deploy · no job dependencies declared
- actions/checkout@v3
actions/checkout@v3 - Setup Terraform
hashicorp/setup-terraform@v2.0.3 - Use Node.js 22.x
actions/setup-node@v3 - Fetch Account ID
if [[ -n "${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" ]]; then ACCOUNT_ID="${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" echo "Using provided CLOUDFLARE_ACCOUNT_ID from secrets." else ACCOUNT_ID=$(curl -X GET "https://api.cloudflare.com/client/v4/accounts" -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" -H "Content-Type:application/json" | jq ".result[0].id" -r) if [[ "$ACCOUNT_ID" == "null" ]]; then echo "Failed to get an account id, please make sure you have set up CLOUDFLARE_API_TOKEN correctly!" exit … - Install packages
npm install cd worker npm install - Build worker
cd worker npx wrangler deploy src/index.ts --outdir dist --dry-run - Build page
npx @cloudflare/next-on-pages - Create D1 database and tables
python3 deploy/init_d1.py # This sets D1_ID in GITHUB_ENV - Migrate state from KV (if needed)
python3 deploy/migrate_kv.py - Deploy using Terraform
terraform init DO_RESP=$(curl "https://api.cloudflare.com/client/v4/accounts/$TF_VAR_CLOUDFLARE_ACCOUNT_ID/workers/durable_objects/namespaces?per_page=1000" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN") if echo "$DO_RESP" | jq -e '.result[] | select(.script == "uptimeflare_worker" and .class == "RemoteChecker")' > /dev/null; then echo "Existing Durable Object namespace found for uptimeflare_worker RemoteChecker. No migration needed." else echo "No existing Durable Object namespace found f… - Upload pages
npx wrangler pages deploy .vercel/output/static --project-name uptimeflare
Triggers: issue_comment, issues
build · no job dependencies declared
- usthe/issues-translate-action@v2.7
usthe/issues-translate-action@v2.7
Triggers: workflow_dispatch
Sync latest commits from upstream repo · no job dependencies declared
- Checkout target repo
actions/checkout@v3 - Current config
cat uptime.config.ts cp uptime.config.ts /tmp/origin.config.ts - Sync upstream changes
git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" # Fetch latest code git clone https://github.com/lyc8503/UptimeFlare /tmp/latest rm -rf /tmp/latest/.git # Clean current repo and update git rm -rf '*' cp -r /tmp/latest/. . cp /tmp/origin.config.ts uptime.config.ts git add . git commit -m "Sync latest code from upstream" git push - Trigger deployment
benc-uk/workflow-dispatch@v1Condition: ${{ inputs.override-token == '' }}
build: next build
deploy: wrangler deploy
Repository README
View original on GitHub ↗Full upstream document by @lyc8503 · README.md · snapshot a5670e5
✔UptimeFlare
A more advanced, serverless, and free uptime monitoring & status page solution, powered by Cloudflare Workers, complete with a user-friendly interface.
📢 [SECURITY ADVISORY 2026/03/04] A vulnerability (CVE-2026-29779) that could expose monitor configuration and credentials in uptime.config.ts to clients was fixed. Versions between 2025-09-21 (from commit 41257c6) and 2026-03-04 are affected. Affected users are strongly advised to upgrade to the latest version.
🎉 [UPDATE 2026/01/03] I have just migrated UptimeFlare from KV to D1 Database. I also updated the Terraform Cloudflare provider to v5 and improved the deployment process. The data structure has been optimized to resolve long-standing performance issues.
New users can deploy directly, while existing users can have a simple auto migration process (upgrade docs below)! Feel free to open an issue if you run into any trouble deploying.
⭐Features
- Open-source, easy to deploy (in under 10 minutes, no local tools required), and free
- Monitoring capabilities
- Up to 50 checks at 1-minute intervals
- Geo-specific checks from over 310 cities worldwide
- Support for HTTP/HTTPS/TCP port monitoring
- Up to 90-day uptime history and uptime percentage tracking
- Customizable request methods, headers, and body for HTTP(s)
- Custom status code & keyword checks for HTTP(s)
- Downtime notification supporting 100+ notification channels
- Customizable Webhook
- Multi-language support (English/Chinese)
- Status page
- Interactive ping (response time) chart for all types of monitors
- Scheduled maintenances alerts & Incident history page
- Responsive UI that adapts to your system theme
- Customizable status page
- Use your own domain with CNAME
- Optional password authentication (private status page)
- JSON API for fetching realtime status data
👀Demo
My status page (Online demo): https://uptimeflare.pages.dev/
Some screenshots:

⚡Quickstart / 📄Documentation
Please refer to Wiki
🚀Upgrade existing deployments
Get the latest features right away with simple upgrade process
⚙️Docs for developer
To contribute new features or customize your deployment furthermore, see here.
New features (TODOs)
- Specify region for monitors
- TCP
openedpromise - Use apprise to support various notification channels
- Telegram example
- Bark example
- Email notification via Cloudflare Email Workers
- Improve docs by providing simple examples
- Notification grace period
- SSL certificate checks
- Self-host Dockerfile
- Incident history
- Improve
checkLocationWorkerRouteand fix possibleproxy failed - Groups
- Remove old incidents
- Known issue:
fetchdoesn't support non-standard port (resolved after CF update) - Compatibility date update
- Scheduled Maintenance
- Add docs for dev
- Migration to Terraform Cloudflare provider version 5.x
- Cloudflare D1 database
- Scheduled maintenances (via IIFE)
- Simpler config example
- Upcoming maintenances
- Universal Webhook upgrade
- i18n...? (maybe)
- ICMP via proxy?
- Add default UA
- Customizable footer
- New header logo
- Improve CPU time usage
- Local deployment (docs WIP)
Frequently asked about UptimeFlare
What is UptimeFlare?+
UptimeFlare is a self-hosted Better Stack/Pingdom alternative built on the Cloudflare developer platform. Run minute-by-minute uptime checks and your own status page on Cloudflare.
What does UptimeFlare replace?+
UptimeFlare is listed as an alternative to Better Stack, Pingdom, UptimeRobot. Compare the features and tradeoffs before migrating.
What Cloudflare primitives does UptimeFlare use?+
UptimeFlare is built on D1, Durable Objects, Pages, Workers.
How much does UptimeFlare cost to run?+
A small uptime-monitoring setup can fit Cloudflare Free allowances using Workers, Pages, D1 and SQLite Durable Objects. Eligibility depends on measured CPU, monitor count, status-page traffic and account quotas; it is not a verified zero-cost deployment. Keep combined Workers and Pages Function requests within the account Free allowance of 100,000 per day and each Worker invocation within 10 ms CPU. Measure history compaction and checks; the upstream claim of 50 monitors is not a tested free-tier capacity here. Keep D1 below 5 million rows read/day, 100,000 rows written/day and the account storage allowance. The scheduled Worker writes compacted state on status changes or the configured cooldown; page requests also read it. Use the Terraform-created SQLite RemoteChecker class for optional worker:// regional checks and stay within Durable Object request, duration and storage allowances. Other proxy modes and Globalping are separate services with their own terms. Use an eligible GitHub Actions plan and provision your own Cloudflare account, token, database and configuration. Private-repository Actions minutes, custom domains and third-party notification delivery can add costs. Basic direct checks and the public status page need no paid notification provider. Apprise, hosted notification gateways and external proxies are optional and excluded from this free-tier assessment. Check current Cloudflare pricing before deploying.
Is UptimeFlare open source?+
The upstream repository declares the Apache-2.0 license. Read its terms at https://raw.githubusercontent.com/lyc8503/UptimeFlare/a5670e51cbc167bf3610fce4d3389dd00141d729/LICENSE. Source code and contributor credit are available at https://github.com/lyc8503/UptimeFlare.
Community rating
No ratings yet. Tried this project? Share your experience.
One rating per verified account. You can change or remove yours. Accounts are email verified; use of the software is self-reported.




Discussion · 0
sign in to comment →