Self-hosting thump
thump runs entirely on Cloudflare: Workers (API + dashboard + status pages), D1 (config + state), KV (sessions + cache), Durable Objects (probe schedulers + incident coordinator), and Analytics Engine (probe time-series). Everything fits the free Workers plan except the browser monitor kind (real-browser checks), which needs Browser Rendering on a paid plan — every other feature degrades cleanly without it.
This guide takes you from clone to a working deployment on your own Cloudflare account.
1. Prerequisites
- A Cloudflare account (free is fine), with a domain on Cloudflare DNS if
you want custom domains for the app itself or for status pages
- Node 20+, pnpm 9+
wrangler(bundled — every command below runs throughpnpm exec)
git clone https://github.com/rbaljinder/thump && cd thump
pnpm install
cd apps/api
pnpm exec wrangler login
2. Create the Cloudflare resources
pnpm exec wrangler d1 create thump-db
pnpm exec wrangler kv namespace create SESSIONS
Each command prints an id. Open apps/api/wrangler.toml and replace:
[[d1_databases]]→database_idwith your D1 id[[kv_namespaces]]→idwith your KV namespace id[vars]→CF_ACCOUNT_IDwith your account id (wrangler whoami)routes→ your own domains (or delete theroutesblock to use the
default *.workers.dev URL to start)
The Analytics Engine dataset needs no provisioning — it is created on the first write. Durable Objects are declared in the file and created on deploy.
3. Apply the database migrations
pnpm exec wrangler d1 migrations apply thump-db --remote
Idempotent — safe to re-run on every upgrade (the deploy workflow does exactly this before each code deploy).
4. Configure auth (GitHub OAuth)
Create a GitHub OAuth app (github.com → Settings → Developer settings → OAuth Apps):
- Homepage URL:
https://<your-domain> - Callback URL:
https://<your-api-domain>/auth/github/callback
Then set the public half in wrangler.toml [vars] (GITHUB_CLIENT_ID, GITHUB_REDIRECT_URI) and the secret half:
pnpm exec wrangler secret put GITHUB_CLIENT_SECRET
Google OAuth works the same way (GOOGLE_* vars + wrangler secret put GOOGLE_CLIENT_SECRET) and is optional — GitHub-only sign-in is fine. Passkeys need no configuration.
5. Secrets
Set with pnpm exec wrangler secret put <NAME> from apps/api/. Local equivalents go in apps/api/.dev.vars (gitignored).
Core (set these):
| Secret | Powers | Notes |
|---|---|---|
GITHUB_CLIENT_SECRET | Sign-in | From step 4 |
THUMP_VAULT_KEY | Secrets vault encryption at rest | openssl rand -hex 32. Without it, production vault writes fail closed |
CF_API_TOKEN | Uptime charts, latency history, world map | API token with Account Analytics: Read |
RESEND_API_KEY | Email alerts, invitations, status-page subscriber email | resend.com; verify your sending domain, set RESEND_FROM_EMAIL in [vars] |
Optional, feature-gated (skip freely — each degrades with a clear message):
| Secret / var | Powers |
|---|---|
GOOGLE_CLIENT_SECRET (+ GOOGLE_* vars) | Google sign-in |
SLACK_CLIENT_SECRET (+ SLACK_* vars) | "Add to Slack" OAuth install (pasting a webhook URL works without it) |
ANTHROPIC_API_KEY | AI drafting + self-repair for browser synthetics |
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER | SMS + voice-call alert channels |
VAPID_PRIVATE_KEY (+ VAPID_PUBLIC_KEY, VAPID_SUBJECT) | Web push notifications — generate with node scripts/generate-vapid-keys.mjs |
CF_HOSTNAMES_API_TOKEN (+ CF_ZONE_ID var) | Custom domains for status pages (Cloudflare for SaaS; token needs Zone: SSL and Certificates: Edit) |
CF_CACHE_PURGE_TOKEN | Edge-cache purge on incident transitions (Zone: Cache Purge) |
6. First deploy
The Worker serves the built dashboard itself, so build the web app first:
cd ../web && pnpm run build && cd ../api
pnpm exec wrangler deploy
Visit your domain: the landing page should render, and sign-in should round-trip through GitHub. The 1-minute cron trigger (declared in wrangler.toml) starts the incident coordinator automatically.
Browser Rendering (optional, paid plans): the [browser] binding is deliberately absent from the committed wrangler.toml (wrangler v3 rejects it in local dev). To enable browser monitors, append it just before deploying — this is exactly what the CI workflow does:
printf '\n[browser]\nbinding = "BROWSER"\n' >> wrangler.toml
pnpm exec wrangler deploy
git checkout wrangler.toml
7. Continuous deploys (GitHub Actions)
.github/workflows/deploy.yml deploys on every push to main: builds the web app, applies D1 migrations, appends the browser binding, and runs wrangler deploy. To use it on your fork, set two repo secrets:
CLOUDFLARE_API_TOKEN—Workers Scripts: Edit+D1: Edit(+ zone
permissions if using custom domains)
CLOUDFLARE_ACCOUNT_ID— your account id
8. Post-deploy checklist
- Make yourself platform admin (unlocks
/app/adminplatform metrics):pnpm exec wrangler d1 execute thump-db --remote \ --command "UPDATE users SET role='platform_admin' WHERE email='<you>'" - Monitor thump with thump — Settings → "Self-monitoring" bootstraps
monitors for your own deployment.
- Create your first monitors, alert channels, and (optionally) an on-call
schedule — see the monitoring guide and on-call guide.
Upgrading
git pull
pnpm install
…then push to main (CI path) or repeat step 6 (manual path). Migrations are ordered, additive, and idempotent; code and schema deploy together.
Local development
No Cloudflare account needed — everything runs in wrangler's local simulator:
cd apps/web && pnpm run build && cd ../api
pnpm exec wrangler d1 migrations apply DB --local
pnpm exec wrangler dev --port 4001 --local --var DEV_AUTO_LOGIN:true
DEV_AUTO_LOGIN=true signs you in as a local "Dev Admin" without OAuth. See CONTRIBUTING.md for the test suites and project conventions.