Backstage app-config.yaml ${...} env substitution missing in CI
Backstage substitutes ${ENV_VAR} references in app-config.yaml from the process environment. In CI those variables come from secrets or step env, and a missing one leaves the value empty, breaking startup, database, or integrations config.
What this error means
The backend fails to read config, or a downstream feature (database, auth, integration) fails because a "${...}" placeholder resolved to an empty string.
Backend failed to start up Error: Failed to initialize database connection:
missing password
at DatabaseManager ...
# app-config.yaml referenced ${POSTGRES_PASSWORD} but the env var was unsetCommon causes
The referenced env variable is not set in the job
app-config.yaml uses ${POSTGRES_PASSWORD} or ${BACKEND_SECRET}, but the workflow never exported it, so substitution yields empty.
A secret was not passed to the step
The value lives in a repository secret that was not mapped into the step env, so the process cannot see it.
How to fix it
Export the referenced variables from secrets
- List every ${VAR} used in your app-config files.
- Map each to a secret or value in the step env.
- Re-run so substitution resolves to real values.
env:
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
BACKEND_SECRET: ${{ secrets.BACKEND_SECRET }}Provide a CI-specific config with defaults
Use an app-config.local or CI overlay that supplies safe values so substitution never yields empty for non-secret fields.
yarn start --config app-config.yaml --config app-config.ci.yamlHow to prevent it
- Keep an inventory of every ${VAR} used in app-config files.
- Map required secrets into the step env for jobs that read config.
- Fail fast with a config lint that flags unset substitutions.