Environment variables
The handful of variables you need to run Valcraven locally, and where to set them.
This page covers the environment variables you need to get Valcraven running locally, plus the optional ones that switch on individual features. It's the getting-started subset — for the complete list of every variable across the app, deploy, email, AI, and monitoring, see /docs/reference/environment-variables.
Where env vars live
apps/web/.env— the app's runtime variables. Copy it from the example to start:cp apps/web/.env.example apps/web/.env. Docker Compose loads this file into theapp-devandrealtime-devcontainers, and Next.js loads it (along with.env.local) when you run directly with Node. The/configure-ssoskill writes OAuth credentials to.env.local.- A root-level
.env(next todocker-compose.yml) — a few variables are consumed by Docker Compose itself, not the app:TS_AUTHKEY,TS_HOSTNAME, andDEV_PORTfor the Tailscale sidecar. Put those here.
.env and .env.local are gitignored — never commit real secrets. .env.example is committed and documents every variable inline.
To run locally
Only one variable is truly required; the rest have working local defaults.
| Variable | Required | What it does |
|---|---|---|
BETTER_AUTH_SECRET | Yes | Signs auth sessions. Must be 32+ chars — generate with openssl rand -base64 32. |
BETTER_AUTH_URL | Recommended | Better Auth's base URL and a trusted origin. Docker Compose defaults it to http://localhost:3013. Set it explicitly (it isn't reliably inferred behind a proxy), and to your tailnet URL for phone QA. |
APP_URL | Recommended | Public URL used in emails and Stripe/redirect links. Compose defaults it to http://localhost:3013. |
APP_NAME | Optional | Display name in the UI and emails. Defaults to Valcraven. |
DATABASE_PATH | Optional | Local SQLite file path. Defaults to ./data/valcraven.db; Compose sets the in-container path automatically. |
ADMIN_EMAILS | Optional | Comma-separated emails auto-promoted to admin on signup. Set this to your own email to get an admin account. |
That's enough to start the app, sign up, and use the core flows. See the Quickstart for the run commands.
To enable optional features
Each feature stays dormant until you set its variables — the app degrades gracefully when they're absent.
| Feature | Variables | Notes |
|---|---|---|
| OAuth sign-in | GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET, APPLE_CLIENT_ID / APPLE_CLIENT_SECRET, FACEBOOK_CLIENT_ID / FACEBOOK_CLIENT_SECRET | See OAuth setup. Apple is production-only. |
| AI chat | ANTHROPIC_API_KEY | Enables the Claude (BYOK) tier. Read by the realtime Worker; can also be set at runtime from /admin/ai-models. Without it, chat falls back to an open Workers AI model. |
| Realtime Worker auth | AGENTS_INTERNAL_SECRET | Shared secret for the web app ↔ realtime Worker server-to-server calls. Must match apps/realtime/.dev.vars. Used for live notification push and scheduled wakes. |
| Payments | STRIPE_SECRET_KEY, STRIPE_PRICE_ID, STRIPE_WEBHOOK_SECRET | Stripe checkout, subscriptions, and webhooks. |
| Voice (TTS) | ELEVENLABS_API_KEY | Optional text-to-speech for chat. Voice is text-only when unset. |
| Phone QA (Tailscale) | TS_AUTHKEY, TS_HOSTNAME, DEV_PORT | Set in the root .env — read by Docker Compose for the Caddy sidecar. See the Quickstart's "Remote dev" section. |
Production (Cloudflare Workers)
On Cloudflare Workers, secrets are not read from your local .env — set them with wrangler secret put VAR_NAME, and non-secret vars go in the [vars] block of apps/web/wrangler.toml. Email uses the Workers send_email binding (no key needed). See Secrets & env on Cloudflare and Deploy to Cloudflare Workers.