Deployment Checklist ​

The shortest path from "works locally" to a real production URL.

Using a coding agent?

1. Make it work locally first ​

Do not skip this.

bash
npx teenybase deploy --local --yes
npx teenybase dev

Before you deploy, verify at least these:

bash
curl http://localhost:8787/api/v1/health

Then check these in the browser:

  • http://localhost:8787/api/v1/doc/ui
  • your main sign-up flow
  • your main login flow
  • at least one protected write action

2. Log in to Cloudflare ​

Teenybase deploys to your own Cloudflare account (the Workers free plan is enough to start). Authenticate once:

bash
npx wrangler login

In CI or other non-browser setups, set CLOUDFLARE_API_TOKEN (Workers Scripts and D1 edit permissions) and CLOUDFLARE_ACCOUNT_ID instead.

3. Production secrets ​

The first remote deploy generates any missing secrets into .prod.vars with random values and uploads them with the worker:

  • JWT_SECRET
  • JWT_SECRET_USERS
  • ADMIN_JWT_SECRET
  • ADMIN_SERVICE_TOKEN
  • POCKET_UI_VIEWER_PASSWORD
  • POCKET_UI_EDITOR_PASSWORD

To choose your own values, create .prod.vars before the first deploy (for example cp .dev.vars .prod.vars, then replace every value with the output of openssl rand -base64 32). Never reuse the dev defaults. .prod.vars is gitignored; keep it private.

4. First remote deploy ​

Make sure RESPOND_WITH_ERRORS is not set to true in .prod.vars or under vars in wrangler.jsonc, so failed SQL generation/execution errors do not return raw SQL errors to the caller.

bash
npx teenybase deploy --remote --yes

The first remote deploy creates the D1 database (replacing TEENY_AUTO_CREATE in wrangler.jsonc; commit that file afterwards), uploads the secrets, deploys the worker and applies the migrations.

5. Copy the exact production URL ​

deploy --remote prints the worker URL at the end (+ Deployed: https://...) and saves it as apiRoute in infra.jsonc. Copy that exact URL.

Use that exact URL for:

  • your frontend API base URL
  • Google OAuth callback/origin setup

Set appUrl separately based on where users should land after browser auth and where email links should point to:

  • frontend app: set appUrl to the frontend's public URL
  • backend-only app (no frontend yet): set appUrl to your real deployed backend URL

For email verification and password reset, appUrl is also the base for the link templates teenybase embeds in outgoing emails ({appUrl}/reset-password/{token} and {appUrl}/verify-email/{token}). Your frontend hosts those routes. See Email Verification & Password Reset.

6. Update appUrl and deploy again if needed ​

If appUrl was still set to localhost or a placeholder value, fix it now.

ts
export default {
  appUrl: 'https://YOUR_REAL_FRONTEND_URL_HERE',
  // rest of config
}

Then redeploy:

bash
npx teenybase deploy --remote --yes

7. Later secret changes ​

Changing .prod.vars locally does nothing until you upload the new values.

bash
npx teenybase secrets --remote --upload

8. Final pre-launch check ​

Before you hand the URL to users, verify all of these:

  • apiRoute in infra.jsonc is the URL you expect
  • appUrl matches the real deployed URL
  • sign-up works in production
  • login works in production
  • one protected write action works in production
  • if you use Google auth, the provider settings match the deployed URL exactly
  • if you send emails, password reset and verification work end to end
  • .prod.vars contains real secrets, not dev defaults

9. Useful production commands ​

Stream logs from the deployed worker:

bash
npx teenybase logs

Back up the production database:

bash
npx teenybase backup --remote

See the CLI Reference for all commands.