Configuration reference
Every Frontdoor environment variable with its default, the production checklist, cross-origin session checks and the startup log.
On this page
Frontdoor reads its configuration from environment variables, usually through a .env file. Two examples ship with the project: .env.example.docker for Docker and .env.example.node for Node.js.
With Docker, docker-compose.yml sets NODE_ENV, PORT, DATABASE_PATH and the /data folders itself; those cannot be changed from .env.
Environment variables
Server and storage
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
production disables Dev Login and enables secure cookies |
- (production in Docker) |
PORT |
Express server port | 3001 |
DATABASE_PATH |
SQLite file path | ./frontdoor.db |
AVATAR_CACHE_DIR |
Disk cache for user avatars and metadata | ./data/avatar-cache |
APP_ICON_CACHE_DIR |
Disk cache for uploaded app icons and pasted SVG assets | ./data/app-icon-cache |
BRANDING_ASSET_DIR |
Disk storage for the optional company logo | ./data/branding |
PLUGIN_ASSET_DIR |
Disk storage for plugin packages (plugin system currently locked off) | ./data/plugins |
BACKUP_MAX_UPLOAD_MB |
Largest backup file accepted by Check backup and restore. Uploads are streamed to a temporary file, not held in memory. A reverse proxy in front of Frontdoor needs a matching body size limit | 1024 |
AVATAR_CACHE_TTL_DAYS |
Refresh interval for cached Microsoft 365 / Gravatar avatars | 14 |
AVATAR_CACHE_MISS_TTL_DAYS |
Retry interval when no avatar was found | 30 |
TIMEZONE (or TZ) |
Server time zone used for log timestamps. The default for the UI is set in Customize | UTC |
COMMIT_SHA |
Commit recorded in backups (Docker build argument) | - |
Domain and origins
| Variable | Description | Default |
|---|---|---|
BASE_DOMAIN |
Base domain, without protocol | frontdoor.local |
FRONTEND_URL |
Frontend URL with protocol. Also trusted as origin for requests that change data | https://frontdoor.local |
CORS_ALLOWED_ORIGINS |
Comma-separated allowlist of origins that can call Frontdoor with credentials: 'include', for shared-cookie or session-check pages. Leave empty to disable CORS |
- |
Sign-in and sessions
| Variable | Description | Default |
|---|---|---|
DEV_LOGIN |
Enable local Dev Login without Microsoft SSO (ignored in production) | false |
AZURE_CLIENT_ID |
Application (client) ID of the Entra app registration | - |
AZURE_CLIENT_SECRET |
Client secret of the Entra app registration | - |
AZURE_TENANT_ID |
Directory (tenant) ID | - |
AZURE_REDIRECT_URI |
SSO callback redirect URI | https://frontdoor.local/api/auth/microsoft/callback |
JWT_SECRET |
JWT signing secret | - |
JWT_EXPIRES_IN |
JWT token duration | 7d |
JWT_ISSUER |
Issuer of the session and SSO tokens. Satellite apps that verify tokens must use the same value | frontdoor |
IMPERSONATION_TIMEOUT_MINUTES |
Minutes an admin can act as a user before the impersonation ends (clamped to 5–240) | 30 |
ADMIN_EMAILS |
Comma-separated owner emails, promoted automatically | - |
ADMIN_API_KEY |
API key for the bulk endpoints | - |
Microsoft Entra groups and notifications
| Variable | Description | Default |
|---|---|---|
ENTRA_GROUP_SYNC |
Discover Entra group memberships at sign-in and enable the admin import and sync flows | false (true in docker-compose.yml) |
ENTRA_TENANT_REFRESH_MINUTES |
Minimum minutes between two refreshes of the tenant-wide Entra group catalog triggered by sign-ins. A manual sync always refreshes it; 0 refreshes at every sign-in |
360 |
ENTRA_DELETED_GROUP_POLICY |
What happens to the members of an Entra group that Microsoft Entra reports deleted: freeze keeps their access until an admin decides; revoke removes it at the next sign-in or sync. Both flag the group, keep a snapshot and notify admins |
freeze |
NOTIFICATION_RETENTION_DAYS |
Days a resolved admin notification is kept before the daily purge removes it | 30 |
Rate limits
All limits are per IP address.
| Variable | Description | Default |
|---|---|---|
AUTH_RATE_LIMIT_MAX / AUTH_RATE_LIMIT_WINDOW_MS |
Microsoft sign-in endpoints | 5000 / 15 min |
RBAC_RATE_LIMIT_MAX / RBAC_RATE_LIMIT_WINDOW_MS |
Group and user changes | 500 / 15 min |
API_RATE_LIMIT_MAX / API_RATE_LIMIT_WINDOW_MS |
The rest of the API | 6000 / 15 min |
Production checklist
For a real deployment, set at least:
NODE_ENV=production(already set bydocker-compose.yml; Dev Login is always off in production)BASE_DOMAINandFRONTEND_URLAZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TENANT_IDandAZURE_REDIRECT_URI- a unique
JWT_SECRETof at least 32 characters ENTRA_GROUP_SYNCif you want Entra group syncCORS_ALLOWED_ORIGINSif a static page or satellite app on another origin must call Frontdoor with credentials
Recommended:
ADMIN_EMAILSADMIN_API_KEYJWT_EXPIRES_IN- a tenant default time zone in Customize, if you want something other than UTC
If a required value is missing or malformed, Frontdoor stops at startup with a clear log entry instead of failing later with an authentication error.
Cross-origin session checks
CORS_ALLOWED_ORIGINS is meant for pages that only need to know whether the user already has an active Frontdoor session: for example a plain static page or a download page on a sibling domain.
If you host a page on https://contoso.com, set:
CORS_ALLOWED_ORIGINS=https://contoso.com
The page can then ask Frontdoor whether the browser session is authenticated:
<script>
async function checkSession() {
const response = await fetch('https://frontdoor.contoso.com/api/auth/me', {
credentials: 'include',
});
if (response.ok) {
const { user } = await response.json();
console.log('Signed in as:', user.email || user.displayName);
return;
}
window.location.href = 'https://frontdoor.contoso.com/login';
}
checkSession().catch(() => {
window.location.href = 'https://frontdoor.contoso.com/login';
});
</script>
credentials: 'include' tells the browser to send the frontdoor_token cookie with the request. Frontdoor accepts the cross-origin request only if the page's origin is listed in CORS_ALLOWED_ORIGINS.
Startup log
At startup the server prints a redacted summary of its configuration:
Frontdoor startup configuration:
NODE_ENV=production
PORT=3001
DATABASE_PATH=/data/frontdoor.db
BRANDING_ASSET_DIR=/data/branding
BASE_DOMAIN=contoso.com
FRONTEND_URL=https://frontdoor.contoso.com
CORS_ALLOWED_ORIGINS=[empty]
TIMEZONE=UTC
DEV_LOGIN=disabled
AZURE_CLIENT_ID=[set]
AZURE_CLIENT_SECRET=[set]
AZURE_TENANT_ID=[set]
AZURE_REDIRECT_URI=https://frontdoor.contoso.com/api/auth/microsoft/callback
JWT_SECRET=[set]
ADMIN_EMAILS=1 owner configured
Warnings point out defaults and weak but valid settings, for example:
BASE_DOMAIN is not set, defaulting to frontdoor.localJWT_SECRET is shorter than 32 characters; use a longer random secret in production