Skip to main content

Environment Variables

Complete reference for the environment variables read by Flo. They are injected into the container environment; .env feeds Docker Compose, and flo config env set <id> KEY VALUE writes them on managed tenants.

ASP.NET Core configuration keys are also overridable through environment variables using the __ separator (for example Payment:Provider → PAYMENT__PROVIDER, AuditLog:HashSalt → AuditLog__HashSalt).

Production startup refuses to boot when a required variable is missing (Program.cs validates DB_CONNECTION_STRING, JWT_ISSUER, and — in Production — OIDC_SIGNING_CERT_PATH, OIDC_ENCRYPTION_CERT_PATH, FLO_APP_URL; FE_APP_URL, API_BASE_URL, FLO_CUSTOMER_NAME_SHORT and FLO_CUSTOMER_NAME_FULL are validated right after).

Required in Production​

VariableNotes
DB_CONNECTION_STRINGPostgreSQL connection string (see below)
JWT_ISSUEROIDC issuer, e.g. https://api.yourdomain.com
OIDC_SIGNING_CERT_PATHOpenIddict signing certificate (.pfx)
OIDC_ENCRYPTION_CERT_PATHOpenIddict encryption certificate (.pfx)
FLO_APP_URLTenant apex domain, without scheme (e.g. yourdomain.com)
FE_APP_URLFrontend URL, e.g. https://app.yourdomain.com
API_BASE_URLBackend API URL used to build media/upload URLs
FLO_CUSTOMER_NAME_SHORTShort customer name (logo filenames, uppercase recommended)
FLO_CUSTOMER_NAME_FULLFull customer name (emails, UI)
FORWARDED_NETWORKS and/or FORWARDED_PROXIESForwarded-header trust boundary; production refuses to start without one

Database​

VariableRequiredDescription
DB_CONNECTION_STRINGYesFull Npgsql connection string
DB_NAMEComposePostgreSQL database name (consumed by the postgres service)
DB_USERComposePostgreSQL username
DB_PASSWORDComposePostgreSQL password

Connection string format:

Host=<HOST>;Port=5432;Database=<DB>;Username=<USER>;Password=<PASS>;SSL Mode=Disable

Authentication & OIDC​

VariableRequiredDescription
JWT_ISSUERYesToken issuer URL
JWT_AUDIENCEYesToken audience URL
COOKIE_DOMAINProdAuth cookie domain; a leading dot is added automatically (e.g. .yourdomain.com)
COOKIE_NAMENoCookie name. Default FloAuth; shared-zone tenants get FloAuth-<slug> to avoid collisions
OIDC_SIGNING_CERT_PATHProdPath to the OpenIddict signing certificate (.pfx)
OIDC_SIGNING_CERT_PASSWORDProdSigning certificate password
OIDC_ENCRYPTION_CERT_PATHProdPath to the OpenIddict encryption certificate (.pfx)
OIDC_ENCRYPTION_CERT_PASSWORDProdEncryption certificate password
DATAPROTECTION_CERT_PATHNoPKCS#12 certificate used to encrypt the DataProtection key ring stored in the DB
DATAPROTECTION_CERT_PASSWORDNoPassword for the DataProtection certificate

DataProtection keys live in the database (DataProtectionKeys table), not in a per-container volume, so auth cookies survive restarts and ride replication to a DR standby.

Application URLs & CORS​

VariableRequiredDescription
FLO_APP_URLYesApex domain without scheme; the backend derives https://app.<domain> and https://<domain>
FE_APP_URLYesFrontend URL, used in activation/confirmation links and OAuth redirects
API_BASE_URLYesAPI URL used when constructing media URLs
BLOG_URLNoPublic blog/site URL (CORS allow-list, newsletter/public-site helpers, OIDC redirect URIs)
EXTRA_CORS_ORIGINSNoComma-separated extra allowed origins (e.g. Cloudflare Pages .pages.dev previews)

Branding & Whitelabeling​

VariableRequiredDescription
FLO_CUSTOMER_NAME_SHORTYesShort name; uppercased for logo filenames (LOGO_URBINOCASA.png)
FLO_CUSTOMER_NAME_FULLYesDisplay name used in emails and UI
EMAIL_TEMPLATE_BRANDNoEmail template folder prefix: flo (default), pf, rs

FLO_CUSTOMER_NAME_IT / FLO_CUSTOMER_NAME_EN and FORCE_RESPIRASTUDIO_UI are no longer read by any component; they may still be present in older tenant .env files but are inert.

Email​

See Email Configuration for provider setup and the bulk/transactional distinction.

VariableRequiredDescription
EMAIL_SENDER_EMAIL_PROVIDERTransactionalGmail, Outlook, Cloudflare, or a custom name when EMAIL_SENDER_SMTP_HOST is set
EMAIL_SENDER_EMAIL_USERNAMESMTPSMTP username / sending account
EMAIL_SENDER_EMAIL_PSWSMTPSMTP password / app password
EMAIL_SENDER_EMAIL_ALIASNoPublic sender address (falls back to the username)
EMAIL_SENDER_SMTP_HOSTBYO SMTPCustom SMTP relay host (SES, Mailgun, Postmark, …)
EMAIL_SENDER_SMTP_PORTBYO SMTPDefault 587
EMAIL_SENDER_SMTP_SSLBYO SMTPtrue to use SSL
EMAIL_SENDER_SMTP_REQUIRE_TLSBYO SMTPOpt-out: STARTTLS is required unless set to false
BULK_EMAIL_SENDER_ALIASNoFrom-address used only by the newsletter bulk queue (falls back to EMAIL_SENDER_EMAIL_ALIAS)
CF_EMAIL_ACCOUNT_IDCloudflareCloudflare account id (transactional API dispatcher + bulk sender)
CF_EMAIL_API_TOKENCloudflareCloudflare API token for email sending
CF_EMAIL_ZONE_IDNoCloudflare zone id for the email delivery-log analytics read
CF_EMAIL_ANALYTICS_TOKENNoAnalytics-scoped token; falls back to CF_EMAIL_API_TOKEN
SES_REGIONSES bulkAWS region (e.g. eu-west-1)
SES_ACCESS_KEY_IDSES bulkAWS access key
SES_SECRET_ACCESS_KEYSES bulkAWS secret key
SES_CONFIGURATION_SETNoSES configuration set for bounce/complaint event routing
SWEEGO_API_KEYSweego bulkSweego API key (Api-Key header)
EMAIL_LEGAL_BASE_URLNoBase URL for email legal links (privacy/terms)
EMAIL_PRIVACY_POLICY_URLNoFull privacy-policy URL override in email footers
EMAIL_TERMS_URLNoFull terms URL override in email footers

Payments​

Optional, only meaningful with enable_online_payments. Secrets stay in the backend. Checkout also requires enable_bookings and enable_einvoicing.

VariableDescription
PAYMENT__PROVIDERstripe (default) or paypal
PAYMENT__SUCCESSURL / PAYMENT__CANCELURLRedirects after checkout (default: frontend URL)
PAYMENT__STRIPE__SECRETKEYStripe secret key (sk_live_ / sk_test_)
PAYMENT__STRIPE__WEBHOOKSECRETStripe webhook signing secret (whsec_)
PAYMENT__PAYPAL__ENVIRONMENTsandbox or live (empty = live)
PAYMENT__PAYPAL__CLIENTID / PAYMENT__PAYPAL__CLIENTSECRETPayPal OAuth credentials
PAYMENT__PAYPAL__WEBHOOKIDPayPal webhook id for signature verification
PAYMENT__INVOICESELLER__*Invoice seller data (NAME, VATNUMBER, FISCALCODE, REGIMEFISCALE, ADDRESS, STREETNUMBER, POSTALCODE, CITY, PROVINCE, COUNTRY, EMAIL)

Storage (R2 / Local)​

See Media Storage for the upload and optimization pipeline.

VariableRequiredDescription
STORAGE_PROVIDERNolocal (default) or r2
R2_ENDPOINTR2S3-compatible endpoint (https://<account-id>.r2.cloudflarestorage.com)
R2_ACCESS_KEYR2R2 access key id
R2_SECRET_KEYR2R2 secret access key
R2_BUCKETR2Bucket name
R2_CDN_URLR2Public CDN base URL (custom domain, e.g. https://cdn.yourdomain.com)
R2_TENANT_PREFIXR2Tenant key prefix (the tenant id)
UPLOADS_VOLUME_NAMENoDocker volume that persists /app/wwwroot/uploads (used by backup_db.sh)
UPLOADS_PATHNoHost path alternative for uploads backup outside Docker volumes

Google Analytics (Web Analytics)​

VariableRequiredDescription
GOOGLE_ANALYTICS_SA_JSONWeb AnalyticsGoogle service-account JSON as a single-line, minified string. Scoped read-only to GA4 and Search Console; never persisted to a tenant DB

The UI shows the service account's client_email so an admin can grant it access to the GA4 property and Search Console site. See GA4 Setup.

Failover & DR​

VariableDescription
FAILOVER_ENABLEDMaster switch for the primary-lease subsystem. Absent/false leaves every FAILOVER_* key inert
FAILOVER_LEASE_REQUIREDWhen true, the node requires a valid MON lease to start (migrations, storage writes)
FAILOVER_TENANT_IDTenant identifier validated against the lease payload
FAILOVER_NODE_IDNode identifier validated against the lease payload
FAILOVER_LEASE_DIRECTORYAbsolute, tenant-only read-only mount where the lease file is published
FAILOVER_SIGNING_KEY_IDKey id of the MON lease signing key
FAILOVER_SIGNING_PUBLIC_KEY_SPKIBase64 SPKI public key used to verify the lease signature
FAILOVER_MINIMUM_EPOCHReplay protection: leases with a lower epoch are rejected
FAILOVER_DB_ROLE_SEPARATEDDatabase posture flag: app nodes skip EF migrations (an admin-only runner applies them)
FLO_MIGRATIONS_ONLYRuns migrations and exits (admin-only migration runner mode)

The control plane (flo-control, cli/deploy/control/) is a separate service and uses its own FLO_CONTROL_* variables — for example FLO_CONTROL_DR_ENABLED (gates the DR store, keys, workers and routes) and FLO_CONTROL_DR_AUTOMATION_ENABLED. Flo.BE does not read them; see cli/deploy/control/env.example.

Proxy & Client IP​

VariableDescription
FORWARDED_PROXIESComma-separated proxy IPs whose forwarded headers are trusted
FORWARDED_NETWORKSComma-separated CIDR networks whose forwarded headers are trusted (VPS default: 172.16.0.0/12,192.168.0.0/16)
TRUST_CF_CONNECTING_IPWhether a well-formed CF-Connecting-IP header may override the connection IP. Defaults to on outside production, off in production unless explicitly enabled

Security & Operations​

VariableDescription
MAINTENANCE_MODEBoot-time maintenance default (true/false); can be flipped at runtime via the maintenance API
Maintenance__ApiKeyKey for the X-Maintenance-Key header on /api/v1/maintenance/* endpoints
AuditLog__HashSaltSalt for audit-log hashing. Required in production
AUDIT_HASH_SALTSComma-separated salt chain for salt rotation (takes precedence over the single salt)
OTP_HMAC_PEPPERPepper for OTP code hashing; falls back to DB_CONNECTION_STRING when unset
Turnstile__SecretKeyCloudflare Turnstile secret; when set, anonymous public forms/OTP/newsletter endpoints require a CF-Turnstile-Token
Logging__LogsPathOverrides the log directory read by the logs viewer (default /logs)

Runtime & Build Info​

VariableDescription
ASPNETCORE_ENVIRONMENTDevelopment or Production
APP_VERSIONVersion shown by diagnostics; falls back to the assembly version
GIT_COMMIT_HASH / GIT_BRANCH / BUILD_TIMEBuild metadata exposed by diagnostics; injected at image build time
NGINX_SERVER_NAMEserver_name substituted into the Nginx config (nginx container)

Strapi (Legacy CMS)​

Only needed for tenants still using the legacy Strapi-backed blog/real-estate CMS.

VariableDescription
STRAPI_ENABLEDtrue to enable Strapi sync and configuration
STRAPI_URLStrapi API URL (e.g. https://strapi.yourdomain.com)
STRAPI_API_KEYStrapi API token

The CLI may also write STRAPI_BLOG_URL / STRAPI_REALESTATE_URL for multi-instance tenants, but Flo.BE reads only STRAPI_URL / STRAPI_API_KEY. BLOG_EDITOR_STRAPI_API_KEY is consumed at frontend build time, not by the backend.

Example .env File​

# Runtime
ASPNETCORE_ENVIRONMENT=Production

# Database
DB_NAME=flo
DB_USER=flo_user
DB_PASSWORD=secure_password
DB_CONNECTION_STRING=Host=postgres;Port=5432;Database=flo;Username=flo_user;Password=secure_password;SSL Mode=Disable

# Auth / OIDC
JWT_ISSUER=https://api.yourdomain.com
JWT_AUDIENCE=https://api.yourdomain.com
COOKIE_DOMAIN=.yourdomain.com
OIDC_SIGNING_CERT_PATH=/app/certificates/signing-cert.pfx
OIDC_SIGNING_CERT_PASSWORD=change-me
OIDC_ENCRYPTION_CERT_PATH=/app/certificates/encryption-cert.pfx
OIDC_ENCRYPTION_CERT_PASSWORD=change-me

# URLs
FLO_APP_URL=yourdomain.com
FE_APP_URL=https://app.yourdomain.com
API_BASE_URL=https://api.yourdomain.com

# Branding
FLO_CUSTOMER_NAME_SHORT=YOURCOMPANY
FLO_CUSTOMER_NAME_FULL=Your Company Name
# EMAIL_TEMPLATE_BRAND=flo

# Email (Cloudflare example)
EMAIL_SENDER_EMAIL_PROVIDER=Cloudflare
CF_EMAIL_ACCOUNT_ID=your-cf-account-id
CF_EMAIL_API_TOKEN=your-cf-api-token
EMAIL_SENDER_EMAIL_ALIAS=newsletter@yourdomain.com

# Storage (R2)
STORAGE_PROVIDER=r2
R2_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
R2_ACCESS_KEY=your-access-key
R2_SECRET_KEY=your-secret-key
R2_BUCKET=flo-media
R2_CDN_URL=https://cdn.yourdomain.com
R2_TENANT_PREFIX=mytenant

# Proxy trust (behind Traefik)
FORWARDED_NETWORKS=172.16.0.0/12,192.168.0.0/16

# Audit / maintenance
AuditLog__HashSalt=replace-with-a-random-secret
Maintenance__ApiKey=replace-with-a-random-secret
MAINTENANCE_MODE=false
NGINX_SERVER_NAME=app.yourdomain.com

# Optional integrations
# GOOGLE_ANALYTICS_SA_JSON={"type":"service_account",...}
# SWEEGO_API_KEY=...
# SES_REGION=eu-west-1
# SES_ACCESS_KEY_ID=...
# SES_SECRET_ACCESS_KEY=...

See .env.example in the repository and Single-Tenant Deployment for the full deployment flow.