Ambiente
Variáveis de ambiente obrigatórias e opcionais. O schema em apps/server/src/bootstrap/env.ts é a fonte de verdade.
Todas as env vars são validadas por um schema Zod em apps/server/src/bootstrap/env.ts. O servidor falha na inicialização com um erro claro se uma var obrigatória estiver faltando ou malformada. Adapters opcionais sobem como no-op quando sua configuração está vazia — você nunca precisa preencher todas as chaves.
Mínimo para subir
Estas quatro são o piso. Sem elas o servidor sai cedo:
BETTER_AUTH_SECRET=<openssl rand -base64 32>
BETTER_AUTH_URL=http://localhost:3005
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/boilerplate?schema=public
CORS_ORIGINS=http://localhost:3004Gere o auth secret com openssl rand -base64 32. CORS_ORIGINS é uma lista separada por vírgula de origins permitidos para o browser.
Auth providers (opcional)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=Deixe ambos vazios para desabilitar o Google OAuth. O magic link é configurado via seu provider de email — veja o módulo notifications.
Storage (opcional)
STORAGE_PROVIDER=null # null | local | s3 | uploadthing
# S3 / R2 / MinIO
STORAGE_S3_BUCKET=
STORAGE_S3_REGION=
STORAGE_S3_ACCESS_KEY_ID=
STORAGE_S3_SECRET_ACCESS_KEY=
STORAGE_S3_ENDPOINT= # defina para R2/MinIO; deixe vazio para AWS
STORAGE_S3_PUBLIC_BASE_URL= # base do CDN
# Disco local (dev)
STORAGE_LOCAL_ROOT=
STORAGE_LOCAL_PUBLIC_BASE_URL=
# UploadThing
UPLOADTHING_TOKEN=
UPLOADTHING_APP_ID=STORAGE_PROVIDER=null (o default) pula o registro de um adapter, então endpoints que precisam de uploads retornam 503. O provider local constrói URLs públicas a partir de BETTER_AUTH_URL (a URL do server), não APP_URL — uploads são servidos pela API, não pelo Next.js.
Background jobs (opcional)
REDIS_URL=redis://localhost:6379
JOBS_REDIS_URL= # padrão: REDIS_URL
JOBS_PREFIX=bull
BULL_BOARD_ENABLED=true # monta a UI em /admin/queuesSem Redis, o BullMQ é registrado mas nenhum worker conecta. BULL_BOARD_ENABLED=true monta uma UI Bull Board protegida em /admin/queues.
Rate limiting
RATE_LIMIT_REDIS_ENABLED=true # cai para memória quando REDIS_URL está vazio
RATE_LIMIT_READ_PER_MIN=300
RATE_LIMIT_WRITE_PER_MIN=60
RATE_LIMIT_AUTH_IP_PER_MIN=30
RATE_LIMIT_AUTH_EMAIL_PER_15MIN=5Os quatro tiers (read, write, auth-ip, auth-email) têm cada um seu próprio prefixo Redis (rl:read:, rl:write:, etc.) para que os counters nunca colidam. Os handlers do limiter são memoizados sobre a referência RateLimitDeps, fazendo com que o modo memória se comporte de forma idêntica ao modo Redis (um store compartilhado por tier).
Observabilidade (opcional)
SENTRY_DSN= # deixe vazio para desabilitar
SENTRY_TRACES_SAMPLE_RATE=0.1
APP_VERSION=
OTEL_EXPORTER_OTLP_ENDPOINT= # deixe vazio para desabilitar trace export
OTEL_SERVICE_NAME=mern-saas-server
METRICS_ENABLED=true # endpoint /metrics do PrometheusO OTel deve subir antes de Express/Prisma carregarem. O primeiro import em apps/server/src/index.ts é ./otel-init.js por causa disso. Não reordene.
O Sentry server SDK é @sentry/bun (não @sentry/node). logger.error(...) não auto-captura; use o helper captureError(err, ctx) para erros que devem paginar alguém.
Billing (depende do provider)
Defina PAYMENT_PROVIDER como um de stripe, mercado-pago, polar, depois preencha as chaves desse provider:
PAYMENT_PROVIDER=stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
# ou
PAYMENT_PROVIDER=mercado-pago
MERCADO_PAGO_ACCESS_TOKEN=
MERCADO_PAGO_WEBHOOK_SECRET=
# ou
PAYMENT_PROVIDER=polar
POLAR_ACCESS_TOKEN=
POLAR_WEBHOOK_SECRET=Veja Billing para a estratégia e setup de webhooks.
Usage metering (opcional)
Desabilitado por default. Ative a flag depois que o downstream registrar seus meters e um mapa plano → cap.
USAGE_METERING_ENABLED=false # defina "true" para enforçar cotas
USAGE_FREE_PRICE_ID=price_free_synthetic # chave sintética do tier Free; não é um price real do PolarVeja Usage metering para a referência completa.
Adicionando uma nova variável
- Edite
apps/server/src/bootstrap/env.tse estenda o schema Zod. Vars obrigatórias precisam de.min(1); opcionais usam.optional()ou.default(...). - Adicione uma entrada documentada em
apps/server/.env.example. - Leia de
env.YOUR_VAR— nunca deprocess.envdiretamente.