Cloudflare Workers
Run the web app as a Cloudflare Worker through OpenNext, with Convex's cloud as its database, the way careerbot.dev does.
careerbot.dev runs this way: the web app is a Cloudflare Worker built with OpenNext (pnpm cf:build, pnpm cf:deploy),
and Convex runs in Convex's cloud. There's no server to look after, but more steps than Docker, and you build on your
own computer.
What you need
- Node.js 22 and pnpm (
corepack enable pnpm), Git. - A Cloudflare account with a domain on it.
- A Convex project in Convex's cloud (the free plan).
- An OpenRouter key, added in the app once you've signed in.
Install
- 1
Get the code
git clone https://github.com/careerbotdev/careerbot.git cd careerbot pnpm install - 2
Make your Convex project
At dashboard.convex.dev, create a project. In it, Settings > URL & Deploy Key: copy the deployment URL (
https://your-deployment.convex.cloud) and generate a production deploy key. In your terminal:export CONVEX_DEPLOY_KEY='prod:your-deployment|…'Keep the quotes: the key has a
|in it. The commands below use this deployment. Don't keep a.env.localwithCONVEX_DEPLOY_KEYandCONVEX_DEPLOYMENTin the same folder: the Convex CLI refuses both at once. - 3
Deploy CareerBot's functions
npx convex deploy -y - 4
Set the deployment's settings
The backend reads its settings from the Convex deployment's environment variables.
scripts/sync-convex-env.shcopies them there, but itsdevandprodtargets read the maintainer's Infisical project. Set them directly instead.First the keys CareerBot makes for itself, the sign-in keys and the key that encrypts saved API keys:
node docker/convex-keys.mjs jwt > convex-keys.env node docker/convex-keys.mjs master >> convex-keys.env npx convex env set --force --from-file convex-keys.env rm convex-keys.envThen the rest.
CAREERBOT_MODE=self-hostedmakes it a copy you run yourself: username and password sign-in, and the owner adds people.npx convex env set SITE_URL https://careerbot.example.com npx convex env set CAREERBOT_MODE self-hostedOptional ones, as you need them (Keys and accounts):
npx convex env set AUTH_GOOGLE_ID '…' npx convex env set AUTH_GOOGLE_SECRET '…' npx convex env set AUTH_GITHUB_ID '…' npx convex env set AUTH_GITHUB_SECRET '…' npx convex env set SIGNUP_ALLOWLIST 'wren@example.com' npx convex env set GOOGLE_PICKER_KEY '…'The full list
sync-convex-env.shsets isSITE_URL,CAREERBOT_MODE,JWT_PRIVATE_KEY,JWKS,AUTH_GITHUB_ID,AUTH_GITHUB_SECRET,AUTH_GOOGLE_ID,AUTH_GOOGLE_SECRET,SIGNUP_ALLOWLIST,MASTER_KEY_V1,GOOGLE_PICKER_KEY,LIQUIDAI_API_KEYandAUTH_REDIRECT_ORIGINS. What each does: Configuration.If you'd rather keep them in a file: the script's
envtarget uses no Infisical. With the keys in.env:SECRETS_FILE=.env scripts/with-secrets.sh scripts/sync-convex-env.sh env. - 5
Point wrangler.jsonc at your Cloudflare account
wrangler.jsoncis the maintainer's. Change, at the top level:Field Now Yours namecareerbot-devYour Worker's name, such as careerbotaccount_idThe maintainer's account Your Cloudflare account ID (Workers & Pages overview in Cloudflare's dashboard) services[0].service(bindingWORKER_SELF_REFERENCE)careerbot-devThe same as nameroutesdev.careerbot.dev,custom_domain: trueYour hostname, such as careerbot.example.com"name": "careerbot", "account_id": "your-cloudflare-account-id", // … "services": [{ "binding": "WORKER_SELF_REFERENCE", "service": "careerbot" }], "routes": [{ "pattern": "careerbot.example.com", "custom_domain": true }],The
env.productionblock is careerbot.dev's own Worker; delete it.pnpm cf:deploywithout--envdeploys the top level. - 6
Build the Worker
The Worker has no
SITE_URLorNEXT_PUBLIC_CONVEX_URLat run time: the build fixes them in. Set them, andCAREERBOT_MODE, for the build:SITE_URL=https://careerbot.example.com \ NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud \ CAREERBOT_MODE=self-hosted \ pnpm cf:buildWithout
CAREERBOT_MODE=self-hostedthe build is careerbot.dev's: the public website at/, and Google and GitHub sign-in for people with an invite only (SIGNUP_ALLOWLISTisn't used). - 7
Deploy the Worker
npx wrangler login pnpm cf:deployCloudflare adds the custom domain's DNS record and certificate.
- 8
Create the owner account
Make a setup code:
npx convex run account:newSetupCodeIt prints the code (
7KQ4-M2XD, say). Openhttps://careerbot.example.comand use Create your account. Lost the password later?npx convex run account:resetPassword '{"username":"wren"}'gives a temporary one.
Sign-in callbacks
With Convex's cloud, Google and GitHub send people back to the deployment's HTTP actions address:
https://your-deployment.convex.site/api/auth/callback/google
https://your-deployment.convex.site/api/auth/callback/github
Updating
This way follows the same releases as Docker, without the images: you build each release from the code. First read the release notes at careerbot.dev/changelog (do what any marked “Read before updating” ask) and back up (Backups). Then, in the folder you cloned:
git pull
pnpm install
npx convex deploy -y
SITE_URL=https://careerbot.example.com NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud CAREERBOT_MODE=self-hosted pnpm cf:build
pnpm cf:deploy
The functions go to Convex before the new Worker goes out, the order Docker's setup step keeps too. git pull may
conflict with your wrangler.jsonc changes; keep yours.