Configuration
Customer Portal reads its deployment configuration from environment variables. Start from .env.example and keep secrets out of version control. This page explains the main choices; use the configuration reference for every variable, default, precedence rule, and production check.
Required settings
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection string used by Drizzle |
PUBLIC_URL | Public application URL used in links and redirects |
BETTER_AUTH_URL | Base URL used by Better Auth on the server and client |
BETTER_AUTH_SECRET | High-entropy secret used to protect Better Auth state |
Use HTTPS URLs for PUBLIC_URL and BETTER_AUTH_URL in production.
Generate BETTER_AUTH_SECRET independently for each environment with openssl rand -base64 32. Do not put it in client runtime configuration, reuse a documentation value, or rotate it without a tested session-migration plan.
Registration policy
Set PORTAL_REGISTRATION_MODE to one of:
open— visitors may create accounts.invitation-only— registration requires an organization invitation.disabled— public registration is unavailable.
PORTAL_TERMS_URL controls the Terms of Service link shown by the authentication forms.
System administrators
ADMIN_EMAILS is a comma-separated list of accounts that receive the system administrator role. Organization roles are separate: an organization can have owners, administrators, and members without granting system-wide access.
ADMIN_EMAILS=owner@example.com,maintainer@example.com
Social authentication
GitHub and Google sign-in are optional. Enable only providers for which credentials are configured:
PORTAL_GITHUB_ENABLED=true
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
PORTAL_GOOGLE_ENABLED=true
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
Register the production and local callback URLs in the provider dashboard. If a provider is not needed, set its PORTAL_*_ENABLED flag to false.
Email delivery
Platform authentication messages use the platform Resend credentials:
RESEND_API_KEY=
RESEND_FROM_EMAIL=
Feature-specific transactional email can have different ownership. For example, invoice delivery credentials are configured per organization by the timesheets feature.
Feature configuration
Official and local layers are declared explicitly in portal.config.ts. definePortalConfig() converts them into the ordered nuxtLayers consumed by Nuxt extends; localPortalLayer() also records database-provider metadata.
Disabling a layer removes its routes and registrations, but it does not delete its database schema or stored data. See Nuxt layers.