Skip to main content
Version: 0.9.12

Configure environment

platform v0.9.11verified 2026-05-14

Every service ships a vars.yaml that declares every environment variable it needs at runtime, with where the value should come from. Step 2 is to make sure each declared variable has a value waiting for it in either:

  • the bootstrap .env that cloud-init drops at /opt/deployment/.env (variables flagged source: local), or
  • AWS SSM Parameter Store under /${NAMESPACE}/${SERVICE}/<VAR> (non-sensitive), or
  • AWS Secrets Manager under ${NAMESPACE}/${SERVICE}/secrets (or any name-prefixed secret) for sensitive values, or
  • the manifest's default: field (when nothing else provides it), or
  • another secret referenced by secret.arn_from (typically a managed-database master secret — see Managed database secrets).

If any required: true variable is missing from every source, fetch-env.sh aborts and init.sh never starts the containers. That is the safety net for the whole bring-up.

For a deep dive on the manifest schema and resolution order, see:

Two ways to seed

You only need one of these per environment.

The deployment bundle ships a seed-env.sh script that reads .env files and fans out values into SSM and Secrets Manager based on each service's vars.yaml (sensitive entries land in SM, the rest in SSM).

# Preview
./scripts/seed-env.sh \
--namespace voiceai/staging \
--env-dir /tmp/seed/ \
--dry-run

# Apply
./scripts/seed-env.sh \
--namespace voiceai/staging \
--env-dir /tmp/seed/

# Or one service at a time:
./scripts/seed-env.sh \
--namespace voiceai/staging \
--service api \
--env-file /tmp/api.env

The script's --show-required flag prints every variable each service needs (or just one service with --service api --show-required), which is the easiest way to verify completeness before moving on.

Option B — Create SSM / Secrets Manager entries yourself

If you do not use the seed script, create the same shape manually or with your own IaC:

  • one SSM parameter per non-sensitive declared variable, and
  • one Secrets Manager secret per service named ${NAMESPACE}/${SERVICE}/secrets containing a JSON blob with every sensitive variable as a key.

For example:

/${NAMESPACE}/api/REDIS_HOST # SSM
${NAMESPACE}/api/secrets -> {"DATABASE_URL":"...", "REDIS_PASSWORD":"..."} # SM

After the shape exists, populate real values in the AWS Console, CLI, or your secret-management workflow.

Variables flagged with secret.arn_from (e.g. DATABASE_PASSWORD resolved from an RDS master secret) and variables with compose.template (e.g. DATABASE_URL built at runtime) are not created as normal SSM/SM entries — they live elsewhere or are computed.

What to populate per service

The minimum unconditionally required variables per service are summarized below. All of these are required: true in the service's vars.yaml with no default and source other than local — i.e. you must put them in SSM / SM (or rely on a secret.arn_from for them).

ServiceSSM (non-sensitive)Secrets Manager (sensitive)
databasePOSTGRES_PASSWORD, REDIS_PASSWORD
apiREDIS_HOST, DOMAIN_TELPRODATABASE_URL, REDIS_PASSWORD, JWT_SECRET
webREDIS_HOST, NEXTAUTH_URL, DOMAIN_TELWEBDATABASE_URL, REDIS_PASSWORD, NEXTAUTH_SECRET
voiceREDIS_HOST, VAIPROXIESPIPED, SIP_PORT_INTERNAL, SIPPORTTELPRODATABASE_URL, REDIS_PASSWORD
telproREDIS_HOST, DOMAIN_TELPROREDIS_PASSWORD
opsREDIS_HOSTDATABASE_URL, REDIS_PASSWORD
media— (MEDIA_UPLOAD_TOKEN and MEDIA_TLS_* only when used)
squid

Many values are shared across services (REDIS_HOST, REDIS_PASSWORD, DATABASE_URL, DOMAIN_TELPRO). Store them as per-service copies so an operator can rotate one service's view of a value without touching another. Drift between copies is a common bug; if a service can't authenticate to Redis after a rotation, check that REDIS_PASSWORD is the same string in voiceai/<env>/api/secrets, voiceai/<env>/voice/secrets, etc.

For per-service variable details, see the Configuration tab on each service's operations page.

Verifying the seed

Before you provision instances, verify every service can resolve its full env from AWS.

fetch-env.sh accepts --format export and writes export VAR=value lines to stdout (no file). You can run it on any host that has the AWS CLI and IAM grants:

# On any workstation or jumpbox with AWS access:
NAMESPACE=voiceai/staging \
AWS_REGION=eu-central-1 \
/opt/services/common/fetch-env.sh \
--manifest /opt/services/api/vars.yaml \
--bootstrap /opt/deployment/.env \
--format export | wc -l

A successful run logs Exported N variables to process environment. A missing required variable prints FATAL: Required variables missing from SSM/SM/bootstrap: and the list — fix in AWS Console / CLI and re-run.

Sensitive vs non-sensitive — the rule of thumb

When in doubt, assume:

  • Anything that looks like a credential, key, or PEM → Secrets Manager (sensitive: true in vars.yaml).
  • Anything an operator might need to reason about in plain text (host names, ports, feature toggles, log levels) → SSM Parameter Store.

fetch-env.sh reads SSM with --with-decryption so SecureString parameters work too, but the convention is: real secrets in SM, everything else in SSM. The whole vars.yaml schema and a worked secret.arn_from example for managed databases live in Configuration.

Next

Continue to Provision instances.