Validating Backstage configuration with config:check

Backstage config:check and its schema validation catch broken app-config files before a deploy reaches your portal. This page shows how to wire them into CI and keep secrets out of Git.

On this page

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.

ApproachWhat it catchesWhat it missesWho owns it
Local config:check runSyntax errors, missing keys and values that fail the configuration schema before a commitEnvironment-specific values, secrets that exist only in the deployment target, and errors that appear only at runtimeIndividual developers, who run it before pushing changes to app-config files
config:check --strict in CISchema violations and invalid values on every pull request, including unknown keys that a lenient check would ignoreWhether a referenced secret or external host is reachable from the cluster, and drift between production overrides and the base filePlatform or DevOps team, who owns the pipeline definition and the failure policy for merges
Plugin configuration schemasType and shape errors for each plugin's own settings, such as a wrong type for a provider blockCross-plugin conflicts and behavior the schema does not describe, so a valid file can still break sign-in or catalog loadingPlugin authors for the schema, and the Backstage app team for keeping plugins and their schemas on the same version
Staging environment validationStartup failures, failed database connections, broken sign-in redirects and missing secrets as the real deployment reads themProduction-only data volumes, load-dependent issues, and differences in network policy that staging does not sharePlatform team with input from the security team, who approve promotion from staging to production
Secret and environment file reviewSecrets accidentally committed to the repository and environment-specific values placed in the shared base fileSecrets already exposed in Git history, which requires rotation rather than deletion aloneSecurity 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.

  1. 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 --version from the repository root and compare the output with the versions listed in the root package.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.

  2. 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 --strict from 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.

  3. 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.

  4. 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.yaml or a .env.example file, 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.

  5. 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.

  6. 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.

  7. 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.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.