Get Doota running on localhost — no Cloudflare account required for the app
UI, auth, and the local D1 database. (Sending and receiving real mail needs a
deployed instance with a domain; see Self-hosting.)
Requirements
- Node 22+
- pnpm (the repo is a pnpm workspace)
- A Cloudflare account — only needed later, for deploying and for real mail.
Set up
Install dependencies
From the repository root:
npm run installyarn run installpnpm run installbun run installpnpm installCreate your env file
cp .env.example .envFor local dev you only need two values filled in:
ORIGINS="http://localhost:5173"
BETTER_AUTH_SECRET="<32+ random chars>"Generate a secret with:
openssl rand -base64 32See Configuration for every variable.
Apply database migrations
Create and migrate the local D1 database:
pnpm db:migrate:localStart the dev server
pnpm devThe app is now at http://localhost:5173.
Create the first admin (genesis)
Doota has no email path until a domain is onboarded, so the first super-admin is created without email — trust comes from your possession of the instance. Use the CLI:
pnpm --filter doota reset-admin you@external-example.com 'a-strong-password' --name "Your Name"This creates the super-admin, links the password, enrolls TOTP, and prints
an otpauth:// URI plus backup codes to scan into your authenticator. Use
an external address (not one on a domain Doota serves).
Now sign in at /login.
Handy scripts
| Script | Does |
|---|---|
pnpm dev |
Run the web app at localhost:5173 |
pnpm db:migrate:local |
Apply D1 migrations to the local database |
pnpm --filter doota db:studio |
Open Drizzle Studio |
pnpm check |
Auth-boundary check + svelte-check |
pnpm test |
Run the Vitest suite |
pnpm --filter doota reset-admin |
Create / reset the super-admin |
Next steps
- Ready to receive real mail? Follow Self-hosting.
- Want to explore the app first? See Using Doota.