Skip to content

Environment Variables

Strawly configuration lives in a .env file in the deployment repository. The generate-compose step produces docker-compose.generated.yml, which reads these variables at container start. Create your .env from the template:

cp .env.example .env

You can also generate a .env tailored to your enabled modules:

npm run generate-env
cp .env.generated .env

Then fill in the values described below.

Keep .env out of version control

.env holds your database password and signing keys. Make sure it is in .gitignore (it is by default in the deployment repository). Set file permissions to 600 and never commit it.

Generating secrets

JWT_SECRET, CREDENTIALS_ENCRYPTION_KEY, and POSTGRES_PASSWORD must be long, random values. Generate them with:

./scripts/generate-secrets.sh

The script prints values you paste into .env. To generate one manually:

node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"   # JWT_SECRET (48 bytes)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"   # CREDENTIALS_ENCRYPTION_KEY (32 bytes)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"   # INTERNAL_API_TOKEN (32 bytes)

Core variables

These are required for every Strawly deployment.

Initial admin account

Variable Description Example
ADMIN_EMAIL Email for the first admin account, created by the seeder on first startup admin@example.com
ADMIN_PASSWORD Password for that account. Minimum 8 characters with at least one uppercase letter, one digit, and one special character

Once the account exists, both can be removed from .env. The seeder reads ADMIN_PASSWORD only when it creates the account on a fresh database. Changing the value later has no effect on the existing account; change the password in the app under Users instead.

Database

Variable Description Example
POSTGRES_DB Database name strawly
POSTGRES_USER Database user strawly
POSTGRES_PASSWORD Database password. Use a long random value from generate-secrets.sh

The backend builds its DATABASE_URL from POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB against the postgres service. When you use a managed database instead, set DATABASE_URL directly.

Backend

Variable Description Default
BACKEND_PORT Host port for the backend API 3001
JWT_SECRET 48-byte hex string used to sign authentication tokens
JWT_EXPIRATION Token lifetime 24h
CREDENTIALS_ENCRYPTION_KEY 32-byte hex string used to encrypt stored cloud credentials
CORS_ORIGIN Allowed origin for CORS. Set to your frontend URL (e.g. https://strawly.example.com) http://localhost:3000
INTERNAL_API_TOKEN 32-byte hex string that authenticates service-to-service calls between the backend and module services. Must be at least 32 characters and identical in the backend and every module service

Frontend

Variable Description Default
FRONTEND_PORT Host port for the frontend UI 3000

Container registry

Images are published to git.strawly.app/Strawly. Override the full reference per service when mirroring images elsewhere.

Variable Description Example
BACKEND_IMAGE Full backend image reference git.strawly.app/Strawly/strawly-backend:latest
FRONTEND_IMAGE Full frontend image reference git.strawly.app/Strawly/strawly-frontend:latest

Module variables

These are only required when the corresponding module is enabled in strawly-deployment.yml. When you run generate-compose, the frontend also receives a NEXT_PUBLIC_<MODULE>_SERVICE_URL for each enabled module so it can reach the service directly.

Azure Optimizations (optimizations-azure)

Variable Description
OPTIMIZATIONS_AZURE_PORT Host port for the module (default 3002)

Azure Service Principal credentials (tenant, client ID, client secret, subscription) are not set as environment variables. Enter them in the UI under Settings > Credentials, where they are encrypted with CREDENTIALS_ENCRYPTION_KEY before being stored in the database. The module reads and decrypts them at scan time. See Azure Optimizations for credential setup.

Secret rotation

Rotate the following periodically, or immediately if any may have been exposed.

Rotation schedule

Secret Recommended rotation
JWT_SECRET Every 90 days
CREDENTIALS_ENCRYPTION_KEY Every 90 days (requires re-encryption of stored credentials)
INTERNAL_API_TOKEN Every 90 days
POSTGRES_PASSWORD Every 90 days

Rotating JWT_SECRET

Rotating the JWT secret invalidates all active sessions (everyone is logged out).

# 1. Generate a new value and update JWT_SECRET in .env
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

# 2. Recreate the backend
docker compose -f docker-compose.generated.yml up -d --force-recreate backend

Rotating CREDENTIALS_ENCRYPTION_KEY

Cloud credentials stored in the database are encrypted with this key, so rotation invalidates them until they are re-entered.

# 1. Generate a new value
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# 2. Set CREDENTIALS_ENCRYPTION_KEY in the backend .env AND in every module
#    service .env (the values must be identical), then recreate both services
docker compose -f docker-compose.generated.yml up -d --force-recreate backend optimizations-azure
  1. Re-enter the cloud credentials in the UI under Settings > Credentials. Saving encrypts them with the new key and the module picks up the change without a restart. Collector runs fail with a decryption error between the restart and the re-entry, so do both in one sitting.

Rotating INTERNAL_API_TOKEN

# 1. Generate a new value
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# 2. Set INTERNAL_API_TOKEN in the backend .env AND in every module service
#    .env (the values must be identical), then recreate both services
docker compose -f docker-compose.generated.yml up -d --force-recreate backend optimizations-azure

Module calls fail with 401 while the two services hold different token values, so update both .env files before recreating.

Rotating POSTGRES_PASSWORD

# 1. Generate a new value
new_pw=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")

# 2. Update the password inside PostgreSQL
docker exec strawly-postgres \
  psql -U strawly -c "ALTER USER strawly PASSWORD '$new_pw';"

# 3. Set POSTGRES_PASSWORD=$new_pw in .env, then recreate the affected services
docker compose -f docker-compose.generated.yml up -d --force-recreate backend

Incident response: suspected secret exposure

  1. Rotate the compromised secret immediately using the steps above.
  2. Rotate the other secrets as a precaution.
  3. Review access logs for signs of misuse.

Production overrides

For production deployments, also set:

Variable Description
CORS_ORIGIN Your public frontend URL (e.g. https://strawly.example.com)
DATABASE_URL Full PostgreSQL connection string when using a managed database