FangDash
Getting Started

Self-Hosting

Deploy your own instance of FangDash.

TL;DR - FangDash runs entirely on Cloudflare infrastructure (Workers + D1) - Three services to deploy: API, Web, and PartyKit (multiplayer) - bun run ship deploys all three at once

Estimated time: ~30 minutes

FangDash is designed to run on Cloudflare's infrastructure. Here's how to deploy your own instance.

What You'll Need

  • Node.js >= 24
  • Bun 1.3.10
  • Cloudflare account (free tier works)
  • Twitch Developer application (for OAuth sign-in)
  • PartyKit account (for real-time multiplayer)

Step 1 of 3 — Deploy the API

The API runs on Cloudflare Workers with a D1 database.

  1. Create a D1 database via the Cloudflare dashboard (Workers & Pages → D1) or CLI:
bunx wrangler d1 create fangdash-db
  1. Update apps/api/wrangler.toml with your database ID and production domains. The [[d1_databases]] block should look like:
[[d1_databases]]
binding = "DB"
database_name = "fangdash-db"
database_id = "your-database-id-here"
migrations_dir = "drizzle/"

And the production environment variables:

[env.production.vars]
ENVIRONMENT = "production"
BETTER_AUTH_URL = "https://your-api-domain.workers.dev"
WEB_URL = "https://your-web-domain.workers.dev"
  1. Run migrations against the remote database:
bunx wrangler d1 migrations apply fangdash-db --remote
  1. Set your secrets:
bunx wrangler secret put BETTER_AUTH_SECRET --env production
bunx wrangler secret put TWITCH_CLIENT_ID --env production
bunx wrangler secret put TWITCH_CLIENT_SECRET --env production

Generate a strong secret with: openssl rand -base64 32 — paste the output when prompted for BETTER_AUTH_SECRET.

  1. Deploy:
bun run ship:api

Step 2 of 3 — Deploy the Web App

The web app deploys to Cloudflare Workers using OpenNext.

  1. Set your API URL as a build-time environment variable:
NEXT_PUBLIC_API_URL=https://your-api-domain.workers.dev bun run ship:web

Or add it to apps/web/.env.local first:

NEXT_PUBLIC_API_URL=https://your-api-domain.workers.dev
NEXT_PUBLIC_PARTYKIT_HOST=fangdash.your-username.partykit.dev
  1. Deploy:
bun run ship:web

Step 3 of 3 — Deploy PartyKit

PartyKit handles real-time multiplayer WebSocket connections.

  1. Set your PartyKit token:
export PARTYKIT_TOKEN=your-token
  1. Deploy:
bun run ship:party

All three services are deployed! Your FangDash instance should now be live. Visit your web domain to verify everything works.

Deploy Everything at Once

bun run ship

This runs all three deploy commands in sequence.

Environment Variables Reference

API (apps/api)

VariableTypeWhere setDescription
BETTER_AUTH_SECRETSecretwrangler secret putLong random string for signing sessions
TWITCH_CLIENT_IDSecretwrangler secret putTwitch OAuth app client ID
TWITCH_CLIENT_SECRETSecretwrangler secret putTwitch OAuth app client secret
BETTER_AUTH_URLPlaintextwrangler.tomlPublic URL of the API worker
WEB_URLPlaintextwrangler.tomlPublic URL of the web worker (for CORS + redirects)
ENVIRONMENTPlaintextwrangler.toml"development" or "production"

Web (apps/web)

VariableTypeWhere setDescription
NEXT_PUBLIC_API_URLPlaintext.env.local / CI envPublic URL of the API worker
NEXT_PUBLIC_PARTYKIT_HOSTPlaintext.env.local / CI envPartyKit host (e.g. fangdash.user.partykit.dev)

Local Development

Copy the example files and fill in your values:

cp apps/api/.dev.vars.example apps/api/.dev.vars
cp apps/web/.env.example apps/web/.env.local

Setting Up Twitch OAuth

Create a new application

Add both OAuth Redirect URLs: - Local development: http://localhost:8787/api/auth/callback/twitch - Production: https://your-api-domain.workers.dev/api/auth/callback/twitch

Copy the Client ID and Client Secret to your environment variables

The Twitch OAuth redirect URL must match your deployed API domain exactly — including the protocol (https://) and the path /api/auth/callback/twitch. If sign-in redirects to a blank page or error, this is almost always the cause.

Troubleshooting

Error / SymptomCauseFix
OAuth redirects to blank pageRedirect URL mismatch in Twitch dev consoleEnsure the redirect URL matches your API domain exactly, including protocol and /api/auth/callback/twitch path
"Auth not configured" in prodMissing Cloudflare secretsRun all wrangler secret put commands: BETTER_AUTH_SECRET, TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET
D1 "table not found"Database migrations not appliedRun bunx wrangler d1 migrations apply fangdash-db --remote
PartyKit connection refusedWrong host or missing tokenVerify NEXT_PUBLIC_PARTYKIT_HOST matches your PartyKit deployment URL and PARTYKIT_TOKEN is set
CORS errors in browser consoleWEB_URL mismatchUpdate WEB_URL in wrangler.toml [env.production.vars] to match your actual web domain exactly
Admin panel shows "Forbidden"User role is not admin or devRun bun run promote-admin YourUsername --remote (see Admin docs)
Multiplayer rooms won't startPartyKit not deployedRun bun run ship:party and verify the deployment URL
Score submission fails silentlyRate limit hit (429)Wait 60 seconds and retry, or check the response for a 429 status and Retry-After header
"FORBIDDEN" on all actionsAccount is bannedCheck the user's ban status in the admin panel or D1 database
Build fails with type errorsDependencies out of dateRun bun install then bun run typecheck to identify issues

PartyKit Deployment Details

To expand on Step 3 above:

  1. Get a PartyKit token: Sign up at partykit.io, go to your account settings, and generate an API token.

  2. Set the token:

export PARTYKIT_TOKEN=your-token-here
  1. Deploy:
bun run ship:party
  1. Verify deployment: After deploying, PartyKit will output your host URL. It looks like:
fangdash.your-username.partykit.dev
  1. Update your web app: Set NEXT_PUBLIC_PARTYKIT_HOST in apps/web/.env.local to this URL (without https:// prefix). Redeploy the web app for the change to take effect.

  2. Test the connection: Open your web app, start a multiplayer race, and check the browser console for WebSocket connection messages.

On this page