Skip to content

Environment Variables Reference

Settings you change in the app are not environment variables

The settings under Settings → Advanced (Performance, App behaviour, Alert defaults, Compliance policy, Data retention, Automation and Network) are saved in the app. They are not read from environment variables, so setting a variable does not set or lock a value. Four of them are also read from the environment at start-up, because the app needs them before anyone can sign in: LOG_LEVEL, CORS_ALLOWED_ORIGINS, APP_BASE_URL and TRUST_ALL_PROXY_HEADERS. A value saved in Settings takes priority over the variable.


Quick Reference

Required for Production

Variable Description Example
DB_PASSWORD PostgreSQL password openssl rand -hex 16
SECRET_KEY Application secret key openssl rand -hex 32
CERTIFYCLOUDS_LICENSE_KEY Licence key CC-XXXXXXXX-XXXXXXXX-XXXXXXXX
CORS_ALLOWED_ORIGINS Allowed CORS origins https://yourdomain.com

Required for Azure

At least one authentication method:

Method Variables
Managed Identity (system-assigned) AZURE_USE_MANAGED_IDENTITY=true
Managed Identity (user-assigned) AZURE_USE_MANAGED_IDENTITY=true + AZURE_CLIENT_ID (the identity's client ID)
Service Principal AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET

Database Configuration

Variable Description Default Required
DATABASE_URL PostgreSQL connection string - Yes (production)
DB_PASSWORD Database password - Yes (production)
DATABASE_POOL_SIZE Connection pool size Configured internally No
DATABASE_MAX_OVERFLOW Max overflow connections for burst Configured internally No
DATABASE_POOL_RECYCLE Pool recycle time (seconds) Configured internally No
DATABASE_POOL_TIMEOUT Pool timeout (seconds) 10 No

Example:

DB_PASSWORD=<YOUR_SECURE_PASSWORD>
DATABASE_URL=postgresql://<DB_USER>:${DB_PASSWORD}@<YOUR_DB_HOST>:5432/<DB_NAME>


Security Configuration

Variable Description Default Required
SECRET_KEY Application secret for credential encryption + audit-log HMAC - Yes (production)
JWT_SECRET Signs auth tokens issued at sign-in Falls back to SECRET_KEY No (recommended)
COOKIE_SECURE Require HTTPS for auth cookies true No
COOKIE_SAMESITE SameSite cookie policy strict No
APP_BASE_URL Public address of your deployment (scheme and host, no path). Alert emails and webhook messages use it for their "View in CertifyClouds" link and go out without one when it is not set. When it is set, SSO sign-in and the MCP connector also use it, instead of working the address out from each request. Also set in Settings → Advanced → Network, where a saved value takes priority. Not set Recommended

Set APP_BASE_URL so alert links point to your deployment

Alert emails and webhook messages are sent by background workers with no request to work the address out from, so without APP_BASE_URL they carry no "View in CertifyClouds" link. Set it to your real deployment address (e.g. https://certifyclouds.yourcompany.com) with this variable or in the app at Settings → Advanced → Network. While it is not set, SSO sign-in and the MCP connector auto-detect the address from the incoming request, so SSO works without it. Once it is set, they use it instead, so it must be an address your users can reach. Set it if you use a custom domain or a proxy that rewrites the host name.

Generating SECRET_KEY:

openssl rand -hex 32

JWT_SECRET: separate session key for forced sign-out drills

By default the JWT signing key falls back to SECRET_KEY. Setting a separate JWT_SECRET (also openssl rand -hex 32) lets you rotate the auth key in isolation. Useful if you ever need to force every active user to re-authenticate (suspected token compromise, audit response, employee offboarding sweep) without re-encrypting every stored cloud credential. The onboarding .env we generate for new customers pre-populates both. Existing deployments can add it any time; restart invalidates all current sessions and users sign in again.

SECRET_KEY persistence: read this before your first redeploy

SECRET_KEY is the master encryption key for every cloud credential stored in your database: AWS access keys, GCP service-account JSON, B2C tenant secrets, SSO client secrets, SMTP passwords. Lose it and these credentials become unrecoverable; they must be re-entered through the UI.

On every startup, CertifyClouds runs a boot canary: it decrypts one row of encrypted data using the current SECRET_KEY. If decryption fails, the container refuses to start with SECRET_KEY does not match the encrypted credentials stored in the database.

Persist SECRET_KEY somewhere durable:

  • Keep the original .env file from your initial deploy (the onboarding package already writes SECRET_KEY=... there).
  • Or store it in Azure Key Vault and inject as an env var at container deploy time.
  • Do not rely on regenerating it. The auto-generation in deploy-certifyclouds-{aci,cae}.sh only applies to fresh deploys with no existing data.

Recovery if lost: Restore the original SECRET_KEY from a backup. If no backup exists, the only option is to clear the encrypted columns from the database (automation_sync_configs, b2c_tenants, alerts_email_config, sso_config) and re-enter every credential through the UI. See Troubleshooting for the exact SQL.

Authentication Security Hardening

CertifyClouds includes configurable security controls for authentication hardening:

Variable Description Default
ACCOUNT_LOCKOUT_ATTEMPTS Failed sign-in attempts before lockout Configurable
ACCOUNT_LOCKOUT_DURATION_MINUTES Lockout duration Configurable
LOGIN_RATE_LIMIT_PER_MINUTE Max sign-in attempts per minute per IP Configurable
ENABLE_IP_BINDING Bind sessions to IP address false
IP_BINDING_MODE IP binding enforcement mode warn

How long a user can be inactive before they are signed out is set in Settings → Advanced → Performance (Sign-out after inactivity, 5 to 480 minutes, default 60). It is not an environment variable, and a change applies without a restart.

IP Binding

IP binding may cause issues for users on VPN or mobile networks where IP addresses change frequently. Use warn mode to log IP changes without blocking, or block to enforce strict IP binding.


Initial Admin Account

On first startup, if no users exist, CertifyClouds creates an initial admin account:

Variable Description Default Required
INITIAL_ADMIN_USERNAME Initial admin username admin No
INITIAL_ADMIN_PASSWORD Initial admin password - Yes (first startup)

Example:

INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=<YOUR_SECURE_PASSWORD>

Important

Change this password after first sign-in via Settings → Users.


CORS Configuration

Variable Description Default Required
CORS_ALLOWED_ORIGINS Comma-separated allowed origins. Also set in Settings → Advanced → Network (Allowed browser origins), where a saved value takes priority. http://localhost:3000,... No (see note)

Why CORS rarely matters for CertifyClouds deployments

CertifyClouds ships the React frontend and the API from the same container at the same origin. When your users load https://certifyclouds.mycompany.com, the SPA's API calls go to https://certifyclouds.mycompany.com/api/*. Same origin, so the browser never triggers CORS. The allow-list is only consulted for genuinely cross-origin requests, which a stock deployment doesn't generate.

Set CORS_ALLOWED_ORIGINS explicitly only if you split frontend and API onto different hosts (e.g., API on api.mycompany.com, SPA on app.mycompany.com) or you consume the API from another web app on a different domain. For the standard same-origin deployment the default is harmless even with localhost entries in it.

The startup logs only warn when * appears in the list. That's the only setting that actually weakens the API's cross-origin exposure.

Examples:

# Development
CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:3000

# Production with split frontend/API hosts
CORS_ALLOWED_ORIGINS=https://app.mycompany.com,https://api.mycompany.com


Azure Authentication

Supported Methods: CertifyClouds supports Azure CLI, Service Principal, and Managed Identity authentication. Configure the method appropriate for your environment.

Option 1: Local Development (Azure CLI)

Variable Description Default
AZURE_LOCAL_AUTH Use Azure CLI from host false

Usage:

# Run az login first, then:
./start.sh --local
# Or set manually:
AZURE_LOCAL_AUTH=true

Best for: Local development, testing

Option 2: Service Principal

Variable Description Required
AZURE_TENANT_ID Microsoft Entra ID tenant ID Yes
AZURE_CLIENT_ID Service principal client ID Yes
AZURE_CLIENT_SECRET Service principal secret Yes

Usage:

AZURE_TENANT_ID=<YOUR_TENANT_ID>
AZURE_CLIENT_ID=<YOUR_CLIENT_ID>
AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>

Best for: On-premises deployments, non-Azure cloud

Variable Description Default
AZURE_USE_MANAGED_IDENTITY Enable managed identity auth false
AZURE_CLIENT_ID User-assigned identities only: the identity's client ID. The deploy scripts always run CertifyClouds as a user-assigned identity and set this for you. Leave unset for a system-assigned identity. (unset)

Usage:

# System-assigned identity
AZURE_USE_MANAGED_IDENTITY=true

# User-assigned identity: also set its client ID
AZURE_USE_MANAGED_IDENTITY=true
AZURE_CLIENT_ID=<user-assigned-identity-client-id>

Best for: Azure VMs, Azure Container Instances, Azure Kubernetes Service

Note

AZURE_CLIENT_ID without AZURE_CLIENT_SECRET and AZURE_TENANT_ID selects a user-assigned managed identity; it is not treated as a service principal (which requires all three). The CAE/ACI deploy scripts always set this for you.


Azure Subscription Filtering

Choose the subscriptions CertifyClouds scans in Settings → Subscriptions: click Refresh from Azure, select the subscriptions you want, then click Save Selection. With no selection saved, a scan covers every subscription the credential can see. There are no environment variables for this.


Licence Configuration

Variable Description Default Required
CERTIFYCLOUDS_LICENSE_KEY Licence key (CC-XXXXXXXX-XXXXXXXX-XXXXXXXX) - Yes
CERTIFYCLOUDS_LICENSE_SERVER Licence validation server https://license.certifyclouds.com/api/validate No
CERTIFYCLOUDS_LICENSE_GRACE_DAYS Days to keep working from the cached licence when the licence server is unreachable (1 to 30). Used when your licence does not set its own period 7 No

Example:

CERTIFYCLOUDS_LICENSE_KEY=CC-XXXXXXXX-XXXXXXXX-XXXXXXXX
CERTIFYCLOUDS_LICENSE_GRACE_DAYS=7


Email Notifications

Email providers are not environment variables. Sign in as an administrator and configure SMTP or SendGrid at Settings → Advanced → Email. Credentials are encrypted and stored in PostgreSQL, and provider changes take effect without a container restart.


Logging Configuration

Variable Description Default Options
LOG_LEVEL Logging level. Also set in Settings → Advanced → App behaviour (Log detail), where a saved value takes priority. INFO DEBUG, INFO, WARNING, ERROR, CRITICAL
LOG_FILE Log file path (optional) - Any valid path
LOG_MAX_BYTES Max log file size in bytes 10MB Any integer
LOG_BACKUP_COUNT Number of backup log files to retain Configured internally 0-10

Example:

LOG_LEVEL=INFO
LOG_FILE=/var/log/certifyclouds/app.log


Performance Tuning

Settings, not environment variables

The scan timeout, scan retries, cache lifetimes and Microsoft Graph retries are set in Settings → Advanced → Performance. Environment variables do not set or lock them. The variables below are set when you deploy.

Key Vault Scanning

Variable Description Range
DISCOVERY_MAX_WORKERS How many subscriptions a scan reads at the same time 1-20

Automation Rotation (PRO + ENTERPRISE)

Variable Description Range
ROTATION_MAX_WORKERS How many Key Vault reads rotation discovery makes at the same time 1-20

How often the rotation worker checks is set in Settings → Advanced → Workers.

Multi-Cloud Sync (PRO + ENTERPRISE)

Sync intervals are controlled by the background worker and are not configurable via environment variables. AWS and GCP credentials entered in the UI are encrypted using SECRET_KEY before being stored in the database.


Feature Flags

Feature switches are set in Settings → Advanced → App behaviour, for example Send alert notifications, Cache rotation matches and Faster repeat scans. They are not environment variables.

Share operational counts with licensing is on by default. Switch it off in the same place to stop sending the counts with the licence check.


Environment Mode

Variable Description Default Options
ENVIRONMENT Environment name development development, staging, production

Production mode enforces:

  • SECRET_KEY must not be default
  • AZURE_TENANT_ID is required unless you use a managed identity
  • Additional security validations

Docker/Deployment Variables

Variable Description Default
PORT Application port 8080
APP_PORT Host port mapping 8080
APP_VERSION Semantic version (shown in UI footer) 1.8.0
CERTIFYCLOUDS_VERSION Docker image tag latest

Build Metadata

These variables are set during Docker image build and are embedded in the running container. You do not need to set them manually.

Variable Description
BUILD_DATE Image build date (ISO 8601)
GIT_COMMIT Git commit hash (8 characters)

CI/CD Integration

For using cc-scan in GitHub Actions, Azure DevOps, or GitLab CI pipelines, see the CI/CD Integration Guide.


Example .env Files

Development

# Development .env
ENVIRONMENT=development
DATABASE_URL=postgresql://<DB_USER>:<YOUR_SECURE_PASSWORD>@<DB_HOST>:5432/<DB_NAME>
CERTIFYCLOUDS_LICENSE_KEY=<YOUR_LICENSE_KEY>
AZURE_LOCAL_AUTH=true
LOG_LEVEL=DEBUG
CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:3000

Production

# Production .env
ENVIRONMENT=production
DB_PASSWORD=<YOUR_SECURE_PASSWORD>
DATABASE_URL=postgresql://<DB_USER>:${DB_PASSWORD}@<YOUR_DB_HOST>:5432/<DB_NAME>
SECRET_KEY=<YOUR_SECRET_KEY>
CERTIFYCLOUDS_LICENSE_KEY=<YOUR_LICENSE_KEY>

# Azure (choose one)
AZURE_USE_MANAGED_IDENTITY=true
# OR
AZURE_TENANT_ID=<YOUR_TENANT_ID>
AZURE_CLIENT_ID=<YOUR_CLIENT_ID>
AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>

# CORS
CORS_ALLOWED_ORIGINS=https://certifyclouds.mycompany.com

# Logging
LOG_LEVEL=INFO

Validation

CertifyClouds validates configuration on startup. In production mode:

  1. SECRET_KEY must be changed from default
  2. AZURE_TENANT_ID is required unless you use a managed identity
  3. All database connection parameters must be valid

Confirm the service came up (a misconfigured SECRET_KEY, tenant ID or database setting fails at startup, so a healthy response means these checks passed):

curl http://localhost:8080/health | jq .status