A minimal Cloudflare Worker that keeps your free-tier Supabase projects from being paused due to inactivity. Inspired by JonKrone/keep-supabase-alive, rewritten for Cloudflare Workers + Cron Triggers.
- A Cron Trigger fires the
scheduledhandler daily at 00:00 UTC. - The Worker calls the Supabase Management API to list every project in your account, filtered to those with status
ACTIVE_HEALTHY. - For each project it runs
SELECT 1viaPOST /v1/projects/{ref}/database/query, which resets the inactivity timer. - Results are written to Workers logs as structured JSON. If any project fails, the cron run is marked as failed so it surfaces in the dashboard.
A single Personal Access Token covers every project under the account — there is no need to configure each project's anon / service_role key.
Create a Personal Access Token at https://supabase.com/dashboard/account/tokens (format sbp_...). This token grants account-level access, so store it only as a Cloudflare secret.
bun install
bunx wrangler login
bunx wrangler secret put SUPABASE_ACCESS_TOKEN # paste the sbp_... token from the previous step
bun run deployThe cron schedule activates automatically after deploy. To verify immediately, click "Trigger" on the Worker → Triggers page in the Cloudflare dashboard, or set TRIGGER_TOKEN and call the worker over HTTP (see below).
| Variable | Kind | Description |
|---|---|---|
SUPABASE_ACCESS_TOKEN |
secret, required | Supabase Personal Access Token (sbp_...) |
PROJECT_REFS |
var, optional | Comma-separated project ref allowlist; if unset, all ACTIVE_HEALTHY projects are kept alive |
TRIGGER_TOKEN |
secret, optional | See below |
PROJECT_REFS can live under [vars] in wrangler.toml. Set TRIGGER_TOKEN with bunx wrangler secret put TRIGGER_TOKEN.
The fetch handler is exposed for manual keep-alive triggering, and it is fail-closed by default:
TRIGGER_TOKENunset — the fetch handler returns401for every request; manual triggering is fully disabled. Scheduled cron continues to run as usual. This is the recommended production setup.TRIGGER_TOKENset — requests must includeAuthorization: Bearer <token>, validated with constant-time comparison. On match, the worker runs keep-alive immediately and returns the result as JSON. Useful for debugging or external schedulers.
# When TRIGGER_TOKEN is configured
curl -H "Authorization: Bearer $TRIGGER_TOKEN" https://<your-worker>.workers.devEither way, scheduled cron does not go through this auth layer — it is invoked directly by the Cloudflare platform.
cp .dev.vars.example .dev.vars # fill in SUPABASE_ACCESS_TOKEN (and optionally TRIGGER_TOKEN)
bun run dev # wrangler dev --test-scheduledcurl http://localhost:8787hits the fetch handler (gated byTRIGGER_TOKEN).curl http://localhost:8787/__scheduledsimulates one cron run (local only — does not exist after deploy).
The default is once per day (0 0 * * *), well below Supabase's 7-day inactivity threshold. To run less frequently, edit triggers.crons in wrangler.toml, for example every 3 days:
[triggers]
crons = ["0 0 */3 * *"]