Environment variables
Every variable the template reads, where to set it on each platform, and the two access patterns.
Two access patterns#
Public variables are declared in env.schema in astro.config.ts, validated at build time
and imported from astro:env/client. They are inlined into the client bundle and must start with
PUBLIC_.
Server variables are read through process.env via src/lib/env.ts. This is deliberate:
the Better Auth CLI loads src/lib/auth.ts outside Astro, Node scripts import the database
client, and Cloudflare exposes secrets through process.env when nodejs_compat is enabled.
Reference#
| Variable | Scope | Notes |
|---|---|---|
DEPLOY_TARGET |
build | node, vercel, cloudflare, netlify. Auto-detected on the platforms. |
SITE_URL |
build | Optional canonical origin. Defaults to the Vercel/Netlify production URL, then siteConfig.url. |
DATABASE_URL |
runtime | file:./.data/local.db or libsql://…. TURSO_DATABASE_URL (set by Turso’s Vercel integration) is accepted too. |
DATABASE_AUTH_TOKEN |
runtime | Turso token. TURSO_AUTH_TOKEN is accepted too. |
BETTER_AUTH_SECRET |
runtime | ≥ 32 random bytes. |
BETTER_AUTH_URL |
runtime | Public origin visitors use, as a full URL (https://…). |
BETTER_AUTH_TRUSTED_ORIGINS |
runtime | Comma-separated extra origins; wildcards allowed. |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
runtime | Enables GitHub sign-in. |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
runtime | Enables Google sign-in. |
RESEND_API_KEY |
runtime | Magic links, verification, password resets, contact notifications. Unset: console in dev, disabled in production. |
EMAIL_FROM |
runtime | Sender address, e.g. Site <noreply@example.com>. |
CONTACT_TO_EMAIL |
runtime | Recipient of contact notifications. Unset: messages are only visible in /admin/messages. |
ADMIN_EMAILS |
runtime | Comma-separated addresses that get the admin role on sign-up. |
CONTACT_RETENTION_DAYS |
scripts | Age after which pnpm db:prune deletes archived messages (default 365). |
CONTACT_MAX_AGE_DAYS |
scripts | Optional hard cap: pnpm db:prune also deletes messages of any status older than this. Unset: open messages are kept. |
HEALTH_TOKEN |
runtime | Optional bearer token that unlocks the detailed /api/health response for monitors. Without it only administrators see the details. |
NODE_ENV |
runtime | production enables the safety rules (no console email fallback, secret validation). Set by the platforms, the Dockerfile and wrangler.jsonc. |
PUBLIC_ANALYTICS |
build, public | none or vercel. |
Build-time variables must be present during astro build; runtime variables must be present
where the server code runs.
Where to set them#
Copy .env.example to .env. Astro loads it automatically, drizzle.config.ts loads it with
process.loadEnvFile() and the pnpm db:* and admin:promote scripts run Node with
--env-file-if-exists=.env.
Project → Settings → Environment Variables. Mark secrets as sensitive. The Turso integration
from the Vercel Marketplace adds TURSO_DATABASE_URL and TURSO_AUTH_TOKEN itself, and the
application reads those names too. Preview URLs are trusted automatically through VERCEL_URL.
Non-secret variables go in wrangler.jsonc under vars; secrets are set with wrangler secret put NAME. For local development copy .dev.vars.example to .dev.vars.
Site configuration → Environment variables, or netlify env:set. Deploy previews are trusted
through DEPLOY_PRIME_URL.
Pass them to the process (docker run --env-file .env) or use your orchestrator’s secret store.
Nothing is loaded from .env at runtime by the built server.
Adding a variable#
- Add it to
.env.examplewith a comment. - Public: declare it in
env.schema. Server: read it withgetEnv()fromsrc/lib/env.ts. - Document it in this table and, if the platform needs it at build time, in the deploy guide.