Trovella Wiki

Environment Variables

Complete reference for every environment variable in Trovella -- local defaults, CI values, production sources, and which package reads each one.

Trovella uses environment variables for all runtime configuration. This page documents every variable, where it is read, and how its value differs across environments.

Env File Layout

There are two .env files in development, and one on the production VM:

FilePurposeRead By
apps/web/.envPrimary app configurationNext.js dev server, all @repo/* packages at runtime
.env (root)Database scripts onlypnpm db:migrate, pnpm db:seed, pnpm db:studio
/opt/trovella/.env (VM)Production runtimeAll containers via env_file in docker-compose.prod.yml

The root .env exists because database scripts run outside the Next.js process and need DATABASE_URL. Both files should have the same DATABASE_URL value in development.

Variable Reference

Database

VariableLocal DefaultCIProduction Source
DATABASE_URLpostgresql://trovella:trovella_dev@localhost:5433/trovellaSame (ephemeral PG service)Secret Manager trovella-database-url (rewritten to cloud-sql-proxy:5432)

Read by @repo/db in packages/db/src/client.ts via process.env["DATABASE_URL"]. The production value is rewritten by sync-secrets-vm.sh to route through the Cloud SQL Auth Proxy container instead of the Cloud SQL public IP. See VM Secret Sync for the rewrite logic.

Authentication

VariableLocal DefaultCIProduction Source
BETTER_AUTH_SECRETdev-secret-change-in-productionci-test-secret-at-least-32-characters-longSecret Manager trovella-better-auth-secret
BETTER_AUTH_URLhttp://localhost:3000http://localhost:3000Secret Manager trovella-better-auth-url
NEXT_PUBLIC_BETTER_AUTH_URLhttp://localhost:3000Not set (not needed for CI)Static: https://trovella.ai (build arg + sync script)
GOOGLE_CLIENT_IDyour-google-client-idci-placeholderSecret Manager trovella-google-oauth-client-id
GOOGLE_CLIENT_SECRETyour-google-client-secretci-placeholderSecret Manager trovella-google-oauth-client-secret

Read by @repo/auth in packages/auth/src/server.ts. The BETTER_AUTH_SECRET must be at least 32 characters. NEXT_PUBLIC_BETTER_AUTH_URL is a build-time variable -- Next.js inlines it during next build, so it must be set as a Docker build arg (not a runtime env var).

AI Services

VariableLocal DefaultCIProduction Source
ANTHROPIC_API_KEYEmpty (set manually)ci-placeholderSecret Manager trovella-anthropic-api-key
GOOGLE_AI_API_KEYEmpty (set manually)ci-placeholderSecret Manager trovella-google-ai-api-key

Read by @repo/ai in packages/ai/src/client.ts (Anthropic) and packages/ai/src/embedding.ts (Google Gemini). Both clients are lazy singletons that throw on first use if the key is missing. CI uses placeholder values because AI tests are mocked.

Cache

VariableLocal DefaultCIProduction Source
REDIS_URLredis://localhost:6379redis://localhost:6379 (service container)Secret Manager trovella-upstash-redis-url
UPSTASH_REDIS_TOKENNot set locallyNot setSecret Manager trovella-upstash-redis-token

Read by @repo/cache in packages/cache/src/client.ts. Local development uses a plain Redis container. Production uses Upstash Redis, which requires both a URL and a separate token. The REDIS_URL for Upstash includes the TLS endpoint.

VariableLocal DefaultCIProduction Source
TYPESENSE_API_KEYtrovella_dev_keyci-test-keySecret Manager trovella-typesense-api-key
TYPESENSE_URLhttp://localhost:8108http://localhost:8108 (service container)Static: http://typesense:8108 (Docker network)

Read by @repo/search in packages/search/src/client.ts. In production, the Typesense container runs on the Docker network, so the URL uses the service name typesense as the hostname.

Error Tracking

VariableLocal DefaultCIProduction Source
SENTRY_DSNEmptyNot setSecret Manager trovella-sentry-dsn
NEXT_PUBLIC_SENTRY_DSNEmptyNot setBuild arg from Secret Manager (baked into image)
SENTRY_AUTH_TOKENEmptyGitHub SecretGitHub Secret (build-time only)

SENTRY_DSN configures server-side error reporting in apps/web/sentry.server.config.ts. NEXT_PUBLIC_SENTRY_DSN is inlined at build time for client-side reporting. SENTRY_AUTH_TOKEN is the only GitHub Secret -- it authenticates source map uploads to Sentry during the Docker build and is never sent to the VM.

Background Jobs

VariableLocal DefaultCIProduction Source
INNGEST_DEV1Not setNot set (production mode)
INNGEST_EVENT_KEYEmptyNot setSecret Manager trovella-inngest-event-key
INNGEST_SIGNING_KEYEmptyNot setSecret Manager trovella-inngest-signing-key
INNGEST_BASE_URLhttp://localhost:8288Not setStatic: http://inngest:8288

INNGEST_DEV=1 enables Inngest dev mode locally (no auth required). In production, the event key and signing key authenticate communication between the Next.js app and the self-hosted Inngest server.

Email

VariableLocal DefaultCIProduction Source
SMTP_HOSTlocalhostNot setNot yet configured
SMTP_PORT1025Not setNot yet configured
SMTP_FROMdev@trovella.aiNot setNot yet configured
RESEND_API_KEYNot setNot setSecret Manager trovella-resend-api-key

Email is deferred. Locally, Mailpit captures all outbound email on port 1025 with a web UI at port 8025.

Logging

VariableLocal DefaultCIProduction Source
NODE_ENVdevelopment (implicit)Not setStatic: production
LOG_LEVELNot set (defaults to debug in dev, info in prod)Not setNot set (uses default)

Read by @repo/logger in packages/logger/src/logger.ts. When NODE_ENV=production, the logger outputs structured JSON for Cloud Logging. In development, it uses pino-pretty with colorized output.

Runtime Configuration (Production Only)

These variables are set by sync-secrets-vm.sh but are not secrets:

VariableValuePurpose
NODE_ENVproductionEnables production behaviors across all packages
HOSTNAME0.0.0.0Binds Next.js to all interfaces inside the container
PORT3000Next.js listen port
NEXT_PUBLIC_BETTER_AUTH_URLhttps://trovella.aiAuth callback URL (also set as build arg)
TYPESENSE_URLhttp://typesense:8108Docker network address for Typesense
INNGEST_BASE_URLhttp://inngest:8288Docker network address for Inngest
CLOUD_SQL_CONNECTION_NAMEtrovella-prod:us-central1:trovella-prodUsed by the Cloud SQL Auth Proxy container

Package Boundaries

Each package reads only the env vars relevant to its service. The mapping is enforced by convention (there is no centralized env validation schema):

PackageVariables
@repo/dbDATABASE_URL
@repo/authBETTER_AUTH_SECRET, BETTER_AUTH_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
@repo/aiANTHROPIC_API_KEY, GOOGLE_AI_API_KEY
@repo/cacheREDIS_URL
@repo/searchTYPESENSE_API_KEY, TYPESENSE_URL
@repo/loggerNODE_ENV, LOG_LEVEL
@repo/mcpINNGEST_BASE_URL, INNGEST_DEV
apps/webSENTRY_DSN, NEXT_PUBLIC_* vars

All packages access env vars using bracket notation (process.env["VAR_NAME"]) rather than dot notation, following the TypeScript strict indexing convention.

Build-Time vs Runtime Variables

Next.js distinguishes between build-time and runtime environment variables:

PrefixWhen ResolvedHow Set in Production
NEXT_PUBLIC_*Build time (inlined into client JS bundles)Docker ARG in the builder stage
No prefixRuntime (read from process.env on each request)env_file in docker-compose.prod.yml

The Dockerfile declares build args for public variables:

ARG NEXT_PUBLIC_BETTER_AUTH_URL=https://trovella.ai
ARG NEXT_PUBLIC_SENTRY_DSN
ARG SENTRY_AUTH_TOKEN

Changing a NEXT_PUBLIC_* value requires rebuilding the Docker image. Changing a runtime variable only requires restarting the container.

On this page