Environment variables¶
Every setting described in appsettings.json can also be supplied as an environment variable — you do not have to mount a configuration file at all. This is the usual approach when running CxReports in Docker Compose, Kubernetes, AWS ECS, or any environment where configuration and secrets are injected by the platform.
Naming rule¶
Take the path of the setting in appsettings.json and join the levels with a double underscore
(__):
appsettings.json |
Environment variable |
|---|---|
"AppUrl": "..." |
AppUrl |
"ConnectionStrings": { "Database": "..." } |
ConnectionStrings__Database |
"Encryption": { "Key": "..." } |
Encryption__Key |
"RootUser": { "Email": "..." } |
RootUser__Email |
"SmtpServer": { "Throttling": { "ThrottleInterval": 1 } } |
SmtpServer__Throttling__ThrottleInterval |
"Hangfire": { "Dashboard": { "Enabled": true } } |
Hangfire__Dashboard__Enabled |
Always use __, never :
The Section:Key separator you may see in .NET documentation only works on Windows. On Linux —
which is what the CxReports container runs — shells and container runtimes will not let you set a
variable whose name contains a colon, so two underscores is the only form that works
everywhere. A single underscore does not work either: ConnectionStrings_Database is silently
ignored.
Names are case-insensitive, so ROOTUSER__EMAIL and RootUser__Email are equivalent. Matching the
casing used in appsettings.json keeps things readable.
Arrays¶
Array elements are addressed by their index, starting at 0 and contiguous:
ForwardedHeaders__KnownNetworks__0: "172.16.0.0/12"
ForwardedHeaders__KnownNetworks__1: "192.168.0.0/16"
ForwardedHeaders__KnownNetworks__2: "10.0.0.0/8"
GoogleLogin__SupportedDomains__0: "your-domain.com"
Keyed sections¶
Sections keyed by a name — such as the workspaces in Workspace Initialization — use the key as just another path segment:
Init__Enabled: "true"
Init__Workspaces__dev__Name: "Development"
Init__Workspaces__dev__Recreate: "true"
Init__Workspaces__dev__ImportFilePath: "/config/CxReportsExport.json"
Values are always strings¶
Every environment variable is a string; CxReports converts it to the target type it expects. In
Docker Compose, quote the value — an unquoted true is read by YAML as a boolean and Compose rejects
it with contains true, which is an invalid type:
Swagger__Enabled: "true" # correct
ReportGenerationWorkerCount: "4" # correct
ForceHttps: true # Compose error
Precedence¶
Configuration sources are applied in this order, each one overriding the previous:
appsettings.json(shipped inside the image)appsettings.{Environment}.json— for exampleappsettings.Production.json, the file most installations mount as a Docker secret. The environment name comes fromASPNETCORE_ENVIRONMENTand defaults toProductionin the published image.- Environment variables
- Command-line arguments
So environment variables win over any appsettings.*.json file. You can mount a base configuration
file and override only what differs per environment, or skip the file entirely and configure
everything through variables.
Two files are applied after environment variables
appsettings.Docker.json, baked into the image, sets the Puppeteer and Apryse paths for the
bundled Chromium — those specific keys cannot be overridden with environment variables. The same
applies to the optional secrets/appsettings.{Environment}.json file used to inject AWS and
Google Cloud secrets. Every other setting behaves as described above.
Complete Docker Compose example¶
The setup guide mounts an appsettings.Production.json file. Here is
the same deployment configured entirely through environment variables:
services:
app:
image: codaxy/cx-reports:latest
depends_on:
- db
volumes:
- ./logs:/app/Logs
ports:
- "80:8080"
restart: always
environment:
ConnectionStrings__Database: "Host=db;Database=cxreports;Username=postgres;Password=password"
Encryption__Key: "6F761C152A69C34B655BFF6226116AD4"
Encryption__Vector: "A9B2BC02C2FDDE88"
RootUser__Email: "[email protected]"
RootUser__Password: "password"
RootUser__DisplayName: "First User"
AppUrl: "https://reports.example.com"
SmtpServer__Host: "smtp.example.com"
SmtpServer__Port: "587"
SmtpServer__From: "[email protected]"
SmtpServer__EnableSsl: "true"
db:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: cxreports
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
restart: always
volumes:
postgres_data:
Keeping secrets out of docker-compose.yml is done with an env_file:
ConnectionStrings__Database=Host=db;Database=cxreports;Username=postgres;Password=password
Encryption__Key=6F761C152A69C34B655BFF6226116AD4
Encryption__Vector=A9B2BC02C2FDDE88
RootUser__Password=password
Note
Values in an env_file are not quoted and are taken literally to the end of the line, so a
connection string containing ; needs no escaping.
Kubernetes example¶
env:
- name: ConnectionStrings__Database
valueFrom:
secretKeyRef:
name: cx-reports
key: database-connection-string
- name: Encryption__Key
valueFrom:
secretKeyRef:
name: cx-reports
key: encryption-key
- name: AppUrl
value: "https://reports.example.com"
On AWS ECS the same variables are entered as task definition environment variables — see Host on ECS.
Frequently used variables¶
| Variable | Purpose |
|---|---|
ConnectionStrings__Database |
PostgreSQL connection string (required) |
Encryption__Key, Encryption__Vector |
Encryption key and vector (required) |
RootUser__Email, RootUser__Password, RootUser__DisplayName |
Initial administrator account |
LicenseConfiguration__Key, LicenseConfiguration__ServerName |
License key applied at startup |
AppUrl |
Public root URL of the application |
PathBase |
Base path when hosting under a subdirectory |
ForceHttps |
Redirect HTTP requests to HTTPS |
Cookies__SecurePolicy |
SameAsRequest or Always |
ForwardedHeaders__KnownNetworks__0 |
Trusted proxy network behind a load balancer |
SmtpServer__Host, SmtpServer__Port, SmtpServer__Username, SmtpServer__Password, SmtpServer__From, SmtpServer__EnableSsl |
Outgoing email |
ReportGenerationWorkerCount |
Concurrent report-generation workers |
Swagger__Enabled |
Expose the Swagger UI at /swagger |
Hangfire__Dashboard__Enabled |
Expose the Hangfire dashboard at /hangfire |
GoogleLogin__Enabled, GoogleLogin__ClientId, GoogleLogin__ClientSecret |
Google sign-in |
MicrosoftLogin__Enabled, MicrosoftLogin__ClientId, MicrosoftLogin__ClientSecret |
Microsoft sign-in |
PasswordLogin__Enabled |
Enable or disable email/password login |
Authentication__Provider |
Set to Oidc for Okta or Keycloak |
ASPNETCORE_ENVIRONMENT |
Which appsettings.{Environment}.json is loaded (default Production) |
ASPNETCORE_HTTP_PORTS |
Port the application listens on inside the container (default 8080) |
Each of these is documented in full on the appsettings.json page.
Troubleshooting¶
A variable that is ignored is almost always one of these:
- A single underscore instead of
__, or a:separator on Linux. - A misspelled section — the name must match
appsettings.jsonexactly (casing aside). - An array index that does not start at
0. - The setting is one of the
Puppeteer/Aprysekeys applied after environment variables, as noted under Precedence.
To confirm what the container actually received, run docker compose exec app env | sort and check
that the variable is present and spelled as expected.