Validating Backstage configuration with config:check before every deploy
The config:check command in the Backstage CLI loads your app configuration the same way the backend does and reports values that are missing, malformed or outside the schema, so you can catch them before a deploy rather than at startup. Teams need it once their app-config.yaml, environment overrides such as app-config.production.yaml, and plugin settings change often enough that a single typo can stop the backend from booting. Because Backstage is an open source framework licensed under Apache-2.0 and you run it yourself, the configuration is your team's responsibility: no vendor validates your files for you, and the failure shows up in your environment, not in a hosted service.
Start by confirming the exact flags and strict-mode behavior in the current Backstage CLI documentation, since option names and defaults can change between releases. Backstage builds its configuration from multiple files merged in order and from plugin-supplied schemas, so the useful checks are the ones that run the same merge your production process uses, with the same environment variables set. Decide which configuration files your pipeline checks, which environment profile it targets, and whether a failed check blocks the merge. The Backstage architecture overview describes how the backend and its plugins are organized, which helps you map a failing key to the plugin that owns it.
Keep secrets out of the files that config:check reads. Reference them through environment variables or your secret manager and make sure the check runs with placeholder or CI-scoped values, never real credentials. Confirm with your secret store how values are injected at deploy time, because a check that passes locally can still fail in a cluster if a variable is missing. Treat any plugin schema change as a configuration change: when you upgrade a plugin or the core packages, rerun the check against both your default and production profiles before promoting the build.
Which validation approach fits which team
This table compares four places where Backstage configuration can be checked: a developer's machine, CI with strict mode, plugin configuration schemas, and staging before promotion. Read each row for what it catches, what slips through, and which team should own it. The main takeaway is that no single checkpoint is enough, and the cheapest layers should run first so errors surface before anyone deploys.
| Approach | What it catches | What it misses | Who owns it |
|---|---|---|---|
| Local config:check run | Syntax errors, missing keys and values that fail the configuration schema before a commit | Environment-specific values, secrets that exist only in the deployment target, and errors that appear only at runtime | Individual developers, who run it before pushing changes to app-config files |
| config:check --strict in CI | Schema violations and invalid values on every pull request, including unknown keys that a lenient check would ignore | Whether a referenced secret or external host is reachable from the cluster, and drift between production overrides and the base file | Platform or DevOps team, who owns the pipeline definition and the failure policy for merges |
| Plugin configuration schemas | Type and shape errors for each plugin's own settings, such as a wrong type for a provider block | Cross-plugin conflicts and behavior the schema does not describe, so a valid file can still break sign-in or catalog loading | Plugin authors for the schema, and the Backstage app team for keeping plugins and their schemas on the same version |
| Staging environment validation | Startup failures, failed database connections, broken sign-in redirects and missing secrets as the real deployment reads them | Production-only data volumes, load-dependent issues, and differences in network policy that staging does not share | Platform team with input from the security team, who approve promotion from staging to production |
| Secret and environment file review | Secrets accidentally committed to the repository and environment-specific values placed in the shared base file | Secrets already exposed in Git history, which requires rotation rather than deletion alone | Security team sets the rule, and each repository owner enforces it through pre-commit and CI scanning |
Setting up config:check in your pipeline
This sequence turns config:check from a command someone runs by hand into a pull request gate that rejects broken configuration before it reaches a deployment. It confirms the Backstage version and CLI, wires strict validation into CI, supplies placeholder environment files, adds plugin schema coverage, and proves the gate fails on a planted error.
Confirm the Backstage version and CLI
The platform team checks the Backstage release line the portal is built on and confirms that the Backstage CLI in the repository matches it, so every @backstage package resolves to one version. Run
yarn backstage-cli --versionfrom the repository root and compare the output with the versions listed in the rootpackage.json. Also confirm the Node.js version in your CI image against the Node.js releases that the versioning policy supports for your Backstage release. The step is done when the CLI version, the package versions and the CI Node.js version all agree and the output is saved in the pull request notes.Run config:check locally before adding CI
Before touching the pipeline, a developer runs the check on a working copy with the production-style settings to see the baseline. Run
yarn backstage-cli config:check --strictfrom the repository root and read the output for missing required keys, values of the wrong type and unknown keys. If the command reports errors, fix them in the repository or document why they are expected. The step is done when the command exits with code 0 on the default branch, which gives the pipeline a known-good starting point.Add strict config check to pull requests
The CI owner adds a job to the pull request pipeline that runs the same command as the local check, using the same Node.js version and dependency lockfile as the build. The job should fail the pull request when the command exits with a non-zero code, and it should run before image builds or deployments so broken configuration never reaches a later stage. Make the job a required status check in the repository's branch protection settings. The step is done when a pull request with a passing configuration shows a green check and the job appears as required in the merge settings.
Provide environment files without secret values
Config validation often needs the names of environment variables that the real configuration reads, but CI must not receive production credentials. The platform team creates a committed template for each environment, for example
app-config.ci.yamlor a.env.examplefile, that declares every variable name with a clearly fake placeholder value. Real values stay in the CI secret store or the deployment platform's secret manager and are never committed. Confirm with the security team how placeholders are marked and reviewed. The step is done when a fresh clone can run the check in CI without any secret being present, and a repository scan finds no real credentials in the committed files.Add configuration schemas for plugins
Validation is only as complete as the schemas it can read. Each plugin the portal uses should publish a configuration schema, and the plugin owner should check that the schema covers the keys the team sets, including nested keys for integrations and authentication. Plugins that do not declare a schema cannot be checked for typos or wrong types, so the team should list them and decide whether to add coverage, pin a version that provides it, or accept the gap in writing. Confirm the list against the plugin documentation for each installed version. The step is done when every plugin in the portal either validates its keys in the check output or appears on a reviewed gap list.
Break a key on purpose to test the gate
A check that has never failed is not proven. On a throwaway branch, the engineer changes one required key to an invalid value, such as a misspelled key name or a string where a boolean is expected, and opens a pull request. The CI job should fail, and the log should name the offending path. Revert the change, then confirm the pull request turns green again. Record the failing run link in the pull request so reviewers can see the gate works. The step is done when the job fails on the planted error, reports the key path, and passes after the revert.
Document the gate for the owning team
The platform team writes a short runbook in the repository that explains how to run the check locally, which environment template to use, where real secrets come from, and what the common error messages mean. It should also name the owner who reviews changes to the schema gap list and the process for adding a new environment file. Ask the security owner to review the secret handling section before it is merged. The step is done when a developer who did not build the pipeline can run the check locally, reproduce a CI failure, and know who to ask about a missing schema.
Keeping secrets out of committed configuration
Backstage reads its settings from app-config files, and those files are usually committed to Git. The cards below separate what can safely live in version control from what must be injected at runtime, and how CI can validate configuration without ever exposing a real credential.
Reference secrets through environment variables
Backstage config files can read values from environment variables using the ${VAR_NAME} substitution syntax, so a committed file names a secret without containing it. Each referenced variable must exist when config:check runs, or the check fails for a reason that looks like a config error. Confirm with your deployment team which variables each environment sets, and keep one list of them alongside the config files so reviewers can see every external dependency.
Never commit credentials to app-config
Client secrets, database passwords, API tokens and signing keys must not appear in any app-config file, including base files and environment overlays. Overlay files such as app-config.production.yaml are often committed next to the base file, so a password typed there is as exposed as one in the base file. Add a pre-commit or CI scan for common secret patterns and treat any hit as a rotation event, not just a deletion.
Use placeholders when validating
A validation run does not need production credentials to confirm that the file structure, keys and schema are correct. Where the check accepts it, supply dummy values for every referenced variable in the CI job so the run proves the config parses without touching a live system. Make clear in the pipeline which values are placeholders, so a placeholder never gets promoted into a deployed environment.
Keep CI secrets out of logs
Configuration checks print resolved paths and errors, and a careless debug flag or echoed variable can write a secret into build logs that many people can read. Load values from the CI platform's protected secret store, mark them as masked where supported, and avoid steps that print the environment. Confirm with your CI administrators how masking works on your platform and whether logs are retained beyond your policy.
Confirm your secret store integration
Teams often use an external secret manager that injects values into pods or build jobs, and the injection method determines when variables become available. Whether the secret store syncs into Kubernetes secrets, a sidecar or the CI environment changes how you validate and where failures appear. Ask your platform team to document the exact path from store to running process, and test that a rotated value reaches Backstage without a manual edit.
Security risks in configuration files
Configuration files decide who can sign in, what gets logged and which secrets are reachable. The risks below cluster in base and environment-specific files, so they are worth checking in the same pass that runs config:check. Each one maps to guidance in the Backstage authentication documentation.
Committed credentials in app-config files
Client secrets, database passwords and tokens written directly into app-config.yaml or an environment override end up in Git history, where removing them later does not revoke them. Backstage reads these values through environment variable substitution, so the config file should hold a reference such as ${GITHUB_TOKEN} and never the literal value. During review, search committed config for high-entropy strings and confirm that every secret-bearing key resolves from the runtime environment or a secret store before the file reaches a shared branch.
Guest access left on in production
Guest sign-in is convenient in a local sandbox, but a auth.providers.guest block that survives into a production override lets anyone reach the portal without an identity from your directory. The Backstage authentication guidance advises against offering guest access in production. Check that the production override does not enable guest access, and add a validation step that fails the build when the guest provider appears in the production configuration.
Weak or inconsistent sign-in resolvers
When each provider resolves identities differently, the same person can appear as two catalog users, or a sign-in can succeed with an identity that is not tied to a catalog entry. The authentication guidance recommends configuring resolvers so every provider resolves to the same identity. Review the resolvers setting for each enabled provider in the production file, and confirm that they all match the user entities your catalog ingests from the directory.
Logs that print resolved values
Verbose logging and debug output can print the fully substituted configuration, including tokens and connection strings that were masked in the source file. A value that is safe in YAML becomes exposed once it reaches a log aggregator with broad read access. Confirm that logging levels in production stay at the default, that startup output does not dump configuration, and that log shipping rules exclude fields that contain credentials.
Environment overrides that drift from the base
Production overrides often add keys that the base file never defines, so a reviewer checking only the base file misses the risky change. Compare the production file with the base and flag any added auth provider, relaxed origin list, or disabled check. Keep a short list of security-relevant keys that must match across environments, and make config:check run against each environment file so drift shows up as a failure.
Questions teams ask before validating Backstage configuration
Can our team set up config:check validation without outside help?
Yes, most teams with a working Backstage app and CI system can add a configuration check themselves, because the check is a command the Backstage CLI already provides and it runs against the same app code. The harder part is agreeing on which environments are checked, where secrets come from in CI, and which plugin schemas must be present. Teams that already maintain their own Backstage app, know their Node.js version, and have a platform owner for CI usually manage the first version in-house. Outside help tends to pay off when the portal has many custom plugins, several environments, or a deadline for an audit that depends on the pipeline evidence.
How much effort does a configuration check pipeline take, and what drives it?
The effort depends on the number of plugins that ship configuration schemas, the number of custom plugins that need their own schema, the number of environment-specific files, and how cleanly secrets are already separated from committed configuration. A single-environment app with mostly community plugins is a small change to CI. Each additional environment, each custom plugin without a schema, and each secret that is currently hardcoded adds review and testing work. Ask any provider to break the estimate down by these items and to state which parts are one-time setup and which recur when plugins are upgraded.
What access do we need to give an outside team?
A scoped engagement typically needs read access to the Backstage repository, permission to create or edit a CI workflow on a branch, and a way to supply non-production values for environment variables through your secret store. The outside team should not need production secrets to build the check. Ask for a named CI runner or sandbox, a test environment that mirrors production configuration keys, and a review path that your security team approves. Written scope should list each access grant, who owns it, and when it is removed at the end of the work.
Does config:check replace runtime testing of the portal?
No. The check confirms that the configuration files parse, that values match the schemas declared by the app and its plugins, and that required keys are present. It cannot confirm that your identity provider accepts the callback URL, that a database user has the right grants, or that a catalog location is reachable from the cluster. Those behaviors need start-up tests, smoke tests after deploy, and the sign-in and catalog checks described elsewhere on this site. Treat config:check as a fast gate that catches mistakes early, and keep runtime checks for the things only a running instance can prove.
Which parts of a configuration validation engagement are one-time and which recur?
One-time work usually includes designing the environment layering, writing or collecting plugin configuration schemas, wiring the check into CI, and removing secrets from committed files. Recurring work includes re-running the check whenever a plugin or the core packages change, reviewing new configuration keys in upgrade pull requests, and rotating the secrets that feed the environments. Upgrade releases of Backstage follow a monthly main line and a weekly next line, so a check that is not maintained will drift quickly. Ask a provider which of these items are covered by a fixed-scope delivery and which fall under an ongoing support arrangement.
What does a consultant scope and price for this work, and what should a quote include?
DeployOpen works from a written scope, and most engagements are fixed-scope, so the quote follows from the scope rather than from a published rate. Expect the scope to name the Backstage repository and its plugins, the CI platform, the environments to check, the schemas to write or verify, the secret sources, the deliverables such as a pipeline and a short runbook, and the acceptance criteria. Ask for the assumptions behind the quote, the items excluded, the client responsibilities, and the process for changes. Compare quotes on those lines instead of on a single total.
Should configuration be kept in Git, and where do secrets belong?
Non-secret configuration, such as base URLs, feature settings and environment-specific names, can be committed to the repository in environment-specific files, as long as the layering is documented. Credentials, tokens, client secrets and database passwords should be supplied at runtime through environment variables sourced from your secret store, and should never appear in committed files or in the output of the check. The check should fail if a secret-shaped value is found in a file that is committed. Confirm the exact configuration keys and loading order against the Backstage configuration documentation for the version you run.
How DeployOpen scopes Backstage configuration validation
DeployOpen starts with a review of how your Backstage configuration is layered today. In a typical Backstage app, the base app-config.yaml is extended by environment files such as app-config.production.yaml, and plugins add their own keys. We map which keys come from which file, which values are read from environment variables, and where a value could be set in two places with different results. This review decides what the validation has to cover: the schema for each plugin you run, the environment overlays your deployments use, and the secret references that must resolve at startup. The output is a written inventory that your team can check against the repository, so the scope is agreed before any pipeline changes are made.
Next, DeployOpen adds a config:check step to your own CI pipeline, running with --strict so that unknown or misplaced keys fail the build instead of passing silently. The check runs against each environment overlay you deploy, not only the development file, and it runs on the same container image or build artifact that goes to production. Your team keeps ownership of the pipeline, runners and branch rules. DeployOpen also reviews how secrets are supplied: values should come from your secret manager or the platform's injection mechanism, never from committed files, and the pipeline should fail clearly when a referenced secret is missing. Where your environments are hosted in your own cloud accounts or clusters, all of this runs there.
DeployOpen documents the result as a short runbook in your repository: the checks that run, the environments they cover, the owner of each schema, and the steps to add a new plugin's configuration to the validation. Scope and deliverables are agreed in writing as a fixed-scope engagement before work starts, and the timeline and effort follow from that written scope. Upgrades change configuration keys, so the same validation has to be maintained. DeployOpen offers retainers for ongoing upgrades, revalidation after plugin changes and periodic reviews of the pipeline. Ask any partner, including DeployOpen, to show how the configuration check is wired into your pipeline and who will maintain it after handover.

