Skip to content

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:

  1. appsettings.json (shipped inside the image)
  2. appsettings.{Environment}.json — for example appsettings.Production.json, the file most installations mount as a Docker secret. The environment name comes from ASPNETCORE_ENVIRONMENT and defaults to Production in the published image.
  3. Environment variables
  4. 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:

services:
  app:
    image: codaxy/cx-reports:latest
    env_file:
      - ./cx-reports.env
cx-reports.env
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.json exactly (casing aside).
  • An array index that does not start at 0.
  • The setting is one of the Puppeteer / Apryse keys 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.