English | 中文
Deploy Uptimer to Cloudflare using the built-in GitHub Actions workflow.
- A GitHub repository (default branch:
masterormain) - A Cloudflare account
- A Cloudflare API Token with deployment permissions
- Access to repository Settings > Secrets and Variables
Trigger: Push to main/master, or manual workflow_dispatch
File: .github/workflows/deploy.yml
Deploy modes (repository variable UPTIMER_DEPLOY_MODE, or workflow_dispatch input deploy_mode):
| Mode | Default | What gets deployed |
|---|---|---|
pages |
Yes | Worker (API + cron) + Cloudflare Pages SPA |
single_worker |
No | One Worker serving SPA static assets + API (no Pages project) |
Priority for manual runs: workflow_dispatch input deploy_mode > repository variable UPTIMER_DEPLOY_MODE > default pages. The input defaults to auto, which defers to the repository variable.
Deploy mode notes:
- Preload behavior: In
pagesmode, the Pages proxy (apps/web/public/_worker.js) preloads and injects homepage snapshot data into the initial HTML for faster initial render. Insingle_workermode, there is no HTML preload injection; the status page renders after the SPA fetches/api/v1/public/homepageon load.- Switching modes: If switching an existing deployment from
pagestosingle_worker, the existing Pages project remains live in Cloudflare and must be deleted or redirected manually to avoid serving outdated versions.
Steps (in order):
- Install Node + pnpm + dependencies
- Resolve Cloudflare Account ID (reads from config, falls back to API query)
- Compute resource names (Worker / Pages / D1) and validate
UPTIMER_DEPLOY_MODE - Check or create D1 database, inject real
database_idinto tempwrangler.ci.toml - Inject Worker
[assets]into tempwrangler.ci.tomlifsingle_workermode - Run remote D1 migrations
- Deploy the Worker
pages: API-only Worker (default base config without[assets])single_worker: SPA + API uploaded together via injected[assets]
- (Optional) Write Worker Secret:
ADMIN_TOKEN - pages mode only: resolve API base/origin, build Vite SPA, create/deploy Pages project, set Pages secret
UPTIMER_API_ORIGIN
| Name | Required | Description |
|---|---|---|
CLOUDFLARE_API_TOKEN |
Yes | Cloudflare API authentication |
UPTIMER_ADMIN_TOKEN |
Yes | Admin dashboard access key; auto-injected as Worker ADMIN_TOKEN secret |
Token needs
Account / Cloudflare Pages / Editfor the defaultpagesmode. Single-worker mode does not require Pages permissions.
| Name | Description |
|---|---|
CLOUDFLARE_ACCOUNT_ID |
Avoids auto-resolution failures |
Override default naming or switch deploy mode:
| Name | Default | Description |
|---|---|---|
UPTIMER_PREFIX |
Repository name slug | Unified resource name prefix |
UPTIMER_WORKER_NAME |
${UPTIMER_PREFIX} |
Worker name |
UPTIMER_PAGES_PROJECT |
${UPTIMER_PREFIX} |
Pages project name (pages mode only) |
UPTIMER_D1_NAME |
${UPTIMER_PREFIX} |
D1 database name |
UPTIMER_D1_BINDING |
DB |
D1 binding name in Worker |
UPTIMER_DEPLOY_MODE |
pages |
pages or single_worker; also available as workflow_dispatch input deploy_mode |
UPTIMER_API_BASE |
Auto-derived or /api/v1 |
API address (e.g. https://my-worker.example.com/api/v1 or /api/v1); pages mode only |
UPTIMER_API_ORIGIN |
Auto-derived | API origin (e.g. https://my-worker.example.com); /api/v1 appended automatically; pages mode only |
VITE_ADMIN_PATH / UPTIMER_ADMIN_PATH |
— | Custom admin dashboard path |
If no naming variables are set, the workflow uses the repository name slug as the default prefix. This keeps names stable across forks.
API address (
pagesmode): the SPA is built withVITE_API_BASE(defaults to<worker>/api/v1). The Pages advanced-mode worker also proxies/api/*to the Worker whenUPTIMER_API_ORIGINis set.API address (
single_workermode): no configuration needed — frontend and API share the Worker origin; the SPA calls/api/v1relatively.
The workflow creates and updates multiple resources. Your token needs:
- Workers Scripts: deploy and manage secrets
- Cloudflare Pages: create projects and deploy (default
pagesmode) - D1: query, create, and migrate databases
- Account: read account info (for account ID resolution)
- Add
CLOUDFLARE_API_TOKENto repository secrets - Add
UPTIMER_ADMIN_TOKEN(admin dashboard access key) - Add
CLOUDFLARE_ACCOUNT_ID(recommended) - (Optional) Set
UPTIMER_PREFIXto avoid name collisions - (Optional) Set variable
UPTIMER_DEPLOY_MODE=single_worker, or choosedeploy_modewhen running the workflow manually - Push to
master/main, or manually trigger "Deploy to Cloudflare" - Once the workflow succeeds, note the URLs from the logs:
pages: status page on*.pages.dev, API on*.workers.devsingle_worker: status page, admin, and API on the Worker URL
Manual
wrangler deploy:apps/worker/wrangler.tomlcontains no[assets]block, so a directwrangler deploydeploys an API-only Worker. Forsingle_workerdeployments, use GitHub Actions (which injects[assets]intowrangler.ci.toml) or add[assets]manually.
pages: visithttps://<pages-project>.pages.dev(admin at/admin)single_worker: visithttps://<worker-name>.workers.dev(admin at/admin)
# Public API (single_worker: use the same host as the status page)
curl https://<worker-url>/api/v1/public/status
# Admin API
curl https://<worker-url>/api/v1/admin/monitors \
-H "Authorization: Bearer <YOUR_ADMIN_TOKEN>"Use Wrangler to check that key tables exist in D1:
monitors, monitor_state, check_results, outages, settings
- Verify
CLOUDFLARE_API_TOKENis set and valid - Confirm the token has account read permissions
- Set
CLOUDFLARE_ACCOUNT_IDexplicitly to skip auto-resolution
- Check that
UPTIMER_D1_BINDINGmatches the binding inapps/worker/wrangler.toml - Verify migration SQL is idempotent and syntactically correct
- Confirm the
Build Web (Vite, single_worker)step producedapps/web/distbeforeDeploy Worker - Confirm the CI-generated
wrangler.ci.tomlstill has the[assets]block (CI injects it whenUPTIMER_DEPLOY_MODE=single_worker) - Deep links like
/adminrely on SPA fallback; in local dev use the Vite server on:5173
- Confirm the Pages project exists and
Deploy Pagesran after the Worker deploy - Check that Pages secret
UPTIMER_API_ORIGINpoints at the Worker origin (workflow sets this best-effort) - Confirm
apps/web/public/_worker.jsis present in the built Pages output (Vite copiespublic/)
- Confirm
UPTIMER_ADMIN_TOKENwas written to the Worker Secret - Check that the token in the browser's localStorage matches the secret
Prefer redeploying the last known-good commit:
- Find the last green deployment commit
- Re-trigger "Deploy to Cloudflare" from that commit
- If schema changes are involved, add a new forward-compatible migration rather than rolling back
D1 migrations should not be rolled back destructively. If a remote migration was already applied, fix forward with a new migration.
| Workflow | Purpose |
|---|---|
ci.yml |
Quality gate: lint, typecheck, test |
deploy.yml |
Production release |
Recommended branch strategy:
- PRs must pass CI before merging
master/mainonly receives reviewed changes- Releases are triggered automatically on push — no manual drift