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:
You can also generate a .env tailored to your enabled modules:
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:
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
- 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¶
- Rotate the compromised secret immediately using the steps above.
- Rotate the other secrets as a precaution.
- 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 |