Skip to content
Dirework

Deployment

Put Dirework online on Cloudflare Workers

Dirework runs on Cloudflare Workers with a D1 (SQLite) database — two small workers and a database, all on Cloudflare's free plan. No servers, no Docker, no database to babysit.

The short version: create a Cloudflare API token, set six GitHub secrets and two required repository variables, push to main. GitHub Actions deploys both workers and the database for you.

What gets deployed

PieceWhereWhat it does
direworkdirework.<your-subdomain>.workers.devThe web app — dashboard, overlays, bot page
dirework-apidirework-api.<your-subdomain>.workers.devThe API — sign-in, tRPC, bot OAuth
dirework-dbCloudflare D1Your tasks, timer, and settings

The web app proxies sign-in and dashboard traffic to the API worker behind the scenes, so everything works from the one dirework.…workers.dev address.

Before you start

Step 1 — Create a Cloudflare API token

  1. Cloudflare dashboard → My Profile → API Tokens → Create Token
  2. Use the Edit Cloudflare Workers template, and add D1 → Edit permissions
  3. Copy the token — this is CLOUDFLARE_API_TOKEN

Step 2 — Tell Twitch about your domain

In the Twitch Developer Console, open your app and add these two redirect URLs (swap in your workers.dev subdomain):

https://dirework.<your-subdomain>.workers.dev/api/auth/callback/twitch
https://dirework.<your-subdomain>.workers.dev/api/bot/callback/twitch

You can keep your localhost URLs in the list too, so local development still works.

Step 3 — Set your GitHub secrets

In your repository: Settings → Secrets and variables → Actions → New repository secret.

SecretValue
CLOUDFLARE_API_TOKENthe token from Step 1
ALCHEMY_PASSWORDany long random string — encrypts deploy state (openssl rand -base64 32)
ALCHEMY_STATE_TOKENa unique random token (min 32 characters) for the account-wide alchemy-state CI store worker; reuse it only across apps intentionally sharing that same worker
BETTER_AUTH_SECRETanother long random string (openssl rand -base64 32)
TWITCH_CLIENT_IDfrom the Twitch console
TWITCH_CLIENT_SECRETfrom the Twitch console

Generate fresh secrets for production — never reuse the ones from your computer.

And set these repository variables

Switch to the Variables tab (same Settings → Secrets and variables → Actions page) → New repository variable. Swap in your own workers.dev subdomain:

VariableValue
BETTER_AUTH_URLhttps://dirework.<your-subdomain>.workers.dev (your web origin)
CORS_ORIGINhttps://dirework.<your-subdomain>.workers.dev (same web origin)

Optional repository variables are supported too; leave them unset to use the defaults:

VariablePurpose
DOCS_URLHTTPS documentation origin used by !dwhelp
PRIVACY_POLICY_URLHTTPS privacy-policy page linked from the footer
TERMS_OF_SERVICE_URLHTTPS terms page linked from the footer

The deploy workflow reads these via ${{ vars.* }} — you don't edit the workflow file. It validates every required value, minimum secret lengths, same-origin HTTPS URLs, optional external URLs, and disabled development-login flags before touching Cloudflare. Invalid configuration fails before any deployment begins.

There is deliberately no NEXT_PUBLIC_SERVER_URL variable. Alchemy resolves the API worker's URL during the deploy and injects it into both the web build and the runtime binding. Setting it as a repository Variable does nothing — and a stale value there is actively misleading when you're debugging. It's a local-development variable only.

Step 4 — Push to main

That's it. Every push to main runs the full verification pipeline — dependency audit, lint, type check, tests with coverage thresholds, and a production build — and only then deploys via Alchemy: both workers, the D1 database, and its migrations. CodeQL also runs extended static security analysis on pushes, pull requests, and weekly. You can also trigger a deploy by hand from the Actions tab (Deploy → Run workflow).

✅ Visit https://dirework.<your-subdomain>.workers.dev. You should see the Dirework welcome screen. Click Claim with Twitch to become the owner.

Deploying from your own machine (optional)

Prefer the command line? Put the same values in packages/infra/.env, then:

bun install
bun run deploy

To tear everything down again: bun run destroy.

After you're live — quick checklist

  • Both Twitch redirect URLs point at your dirework.…workers.dev domain
  • You can sign in and claim the instance
  • Your bot account connects (Dashboard → Bot)
  • The bot page is added to OBS as a browser source (or pinned in a tab) — the bot only listens to chat while that page is open. See Chat commands.
  • The timer and task list overlays show up in OBS
  • Exactly one bot page is open; multiple copies can process the same command twice
  • Bot and overlay URLs are kept private and regenerated after any suspected disclosure
  • main is protected and requires the Verify and CodeQL workflows before merge
  • GitHub secret scanning and push protection are enabled
  • GitHub and Cloudflare accounts use MFA and least-privilege access

Free plan limits (you're fine)

Workers' free plan allows 100,000 requests/day. Overlays poll every few seconds only while OBS has them open — a six-hour stream uses roughly 15–25k requests. D1's free tier (5M reads/day) barely notices a single streamer. If you somehow outgrow it, the $5 Workers paid plan raises every limit.

On this page