Backstage well-known annotations for linking entities to tools

Well-known annotations tell Backstage which tool holds an entity's docs, code, builds, quality data and secrets. This page covers how to set them, which ones to standardize, and which deprecated keys to clean up.

On this page

Which Backstage annotations switch on tool links for each entity

When entities exist in the catalog but their plugin tabs stay empty, the usual cause is a missing annotation in the entity's catalog-info.yaml. Well-known annotations switch on plugin features for each entity. Each annotation is a key-value pair under metadata.annotations that a specific plugin reads to find the tool behind that entity, such as a documentation source, a repository, a CI pipeline or a scanner project. The catalog stores these values without checking that they point to anything real, so a typo in a key or a wrong project slug produces an empty tab with no error in the catalog itself. Treat the annotation as a contract between the descriptor and the plugin, and confirm each key against the documentation for the plugin you install.

Teams usually need annotations in four areas. TechDocs needs backstage.io/techdocs-ref to locate the documentation for an entity, and source-location annotations let plugins find the code behind it. CI and delivery plugins need the identifier of the pipeline or project they monitor. Code scanning plugins need a project key that matches the scanner's own naming, and secrets integrations such as Vault need a path or key reference that the Backstage backend is permitted to read. Decide which of these tools your organization actually uses before adding annotations to every entity, because each annotation you add is one more value that must stay correct as repositories and projects move. Confirm the exact key names and value formats for your plugin versions in the Backstage plugin documentation, since keys change between releases.

Before rolling annotations out across many descriptors, check how your existing entities are labeled. Some keys have been deprecated, and the older form may still appear in templates or in descriptors copied from earlier examples. Search your catalog files for deprecated keys, replace them with the current names, and then verify one entity per tool by opening its tab. A tab that loads with the right data confirms the annotation works. A tab that shows the plugin's empty state usually means the key is missing, misspelled or pointing at a resource the plugin cannot reach.

The annotation families teams use most

Well-known annotations sit in an entity's metadata and tell a plugin which external system holds the matching data. Each family below connects the catalog to a different kind of tool, and each one only works once the entity and the target system are set up correctly.

Source code and location

The github.com/project-slug annotation points the GitHub plugins at a repository, while backstage.io/source-location tells TechDocs and other readers where the source lives. The value must match the repository path exactly, including the owner and project name. Check that the Backstage integration for your Git host is configured and that the token can read the repository, because a correct annotation still shows nothing when the integration cannot authenticate.

Documentation source reference

The backstage.io/techdocs-ref annotation tells TechDocs where to fetch the documentation for an entity. Common values point to the local directory of the source repository or to an external URL, and the choice changes how builds and updates run. Confirm the accepted reference formats for your Backstage version and make sure the docs folder contains the MkDocs configuration that TechDocs expects.

Delivery pipeline keys

Jenkins and other CI annotations link an entity to the pipelines, jobs or builds that deliver it, so teams see build status beside the code and docs. The annotation value must match the job or project identifier in the CI system, and the plugin needs credentials with read access to that system. Confirm the exact key names and the expected value format in the plugin documentation for your version, since CI plugins change more often than core catalog features.

Code quality project key

The SonarQube annotation maps an entity to a project in SonarQube, so the catalog can show quality gates, bugs, coverage and vulnerability counts. The project key in SonarQube must match the annotation value, and the plugin needs a server URL and token configured by the platform team. Confirm the key name against the SonarQube plugin documentation, and agree with the security team which projects are allowed to appear in the portal.

Secret store path keys

Vault annotations point an entity at the secret paths, mounts or keys that belong to its service, so teams can find where credentials live without seeing their values. The plugin only displays references, so the access policy in the secret store is what protects the secret. Confirm the exact annotation names and the path format for your version, and check that the reader account can list paths without receiving secret contents.

How to choose annotations per entity type

A component built and documented from one repository is the simplest case, and it is where most annotations belong. The code, the CI pipeline, the docs and the owning team all live in the same place, so the links point to assets the team already changes in its normal workflow. Start from the source location and the docs reference, then add a tool annotation only when the team that owns the repository actually uses that tool.

  • Source location: point the source-location annotation at the repository and path the component is built from, so readers can reach the code.
  • TechDocs reference: set the techdocs-ref annotation to dir:. when the docs sit beside the code in the same repository.
  • CI and quality links: add the GitHub, Jenkins or SonarQube annotations only for the projects that really build and scan this component.
  • Leave off: annotations for tools the team does not use day to day, such as a scanner that is only run by a central group.

Before merging the descriptor, confirm with the owning team that each annotation value matches a project they can open and maintain.

How to add and verify annotations in catalog-info.yaml

This sequence takes a standard set of tool annotations from agreement to a pilot entity, then into the templates that create new components. Each step ends with a check so that a missing key or a broken link is caught before it reaches the wider catalog.

  1. Agree the standard annotation set

    Platform owners and the teams that run each tool meet to decide which annotations the portal will require, which are optional, and which keys each tool expects. Record the agreed list in a short standards document stored next to the templates, with the exact annotation key, the value format and the owner of each tool. The output is a signed-off list, and you know it is done when every tool the portal links to has one named owner and one agreed key for each feature.

  2. Annotate a pilot entity

    Choose one service that already has a working repository, CI pipeline, documentation and monitoring, then add the agreed annotations to its catalog-info.yaml under metadata.annotations. Use the values your tools actually produce, such as the repository path, the pipeline or project identifier, and the documentation reference, and copy them from the tools rather than from memory. The check is a clean commit to the pilot repository and a refreshed entity in the catalog, with no unknown keys left in the file.

  3. Confirm each plugin tab renders

    Open the pilot entity page in the Backstage UI and visit each tab or card that depends on an annotation, including documentation, CI/CD, code quality and any secrets or deployment views. Confirm that each tab loads live data for this entity and does not show a message asking for an annotation, a permission error or an empty state where data should exist. If a tab stays empty, check whether the plugin is installed and configured in the portal and whether the annotation value matches what the external tool expects, then correct the descriptor and reload the page.

  4. Validate the descriptor before registration

    Run the catalog descriptor validator against the file, and confirm that the YAML parses, the required fields are present and the entity kind and lifecycle values are accepted by your catalog rules. Check that annotation keys are spelled as the plugin documentation states, because a misspelled key is often accepted silently and only shows up as a missing tab. The output is a passing validation run recorded in the pull request, and you know it is done when a deliberately broken key in a test copy is caught by the same run.

  5. Register the entity and review it in the catalog

    Register the pilot descriptor through the catalog location or provider your organization uses, then open the entity in the catalog list and inspect its annotations on the overview page. Confirm that the entity appears once, that its owner and system resolve to existing groups and components, and that the annotation values match the file. If the entity appears twice or the owner shows as unresolved, fix the metadata.name or spec.owner value before rolling out further.

  6. Add the keys to software templates

    Update the software templates that create new components so that the generated catalog-info.yaml includes the agreed annotations, with placeholder values filled from the template parameters or from the new repository's own name. Keep the annotation keys in the template in sync with the standards document, and treat any change to them as a template change that goes through review. The check is a test run of the template that creates a throwaway component and an entity page that shows every expected tab.

  7. Audit existing entities against the standard

    Run a catalog query or a report that lists entities missing one or more required annotations, and use it to plan updates to existing components by owning team. Remove deprecated annotations as you find them, and compare the list with the standards document after each batch of updates. The output is a dated list of gaps and their owners, and you know it is done when the report shows no required annotation missing for entities in the agreed scope.

Which deprecated annotations to find and remove

Deprecated annotations are the keys a plugin used to read before its author renamed or replaced them. When a plugin changes its key, existing catalog-info.yaml files keep the old line because nothing in the catalog rejects an unknown annotation. The entity looks correctly linked in the descriptor, yet the plugin ignores it, so the card stays empty or points at the wrong project. Worse, a team may copy an old key from a neighboring component into a new service and assume the integration works. Start by treating every annotation as suspect until you have matched it to the release that your deployed plugins expect.

Build the list from the source, not from memory. Open the current Backstage release notes and the changelog of each plugin you run, and record every annotation key the release marks as deprecated, renamed or removed, along with the version where the change landed. Confirm the exact keys for your version in those documents, since the key names and the replacements are not fixed here. Then search the catalog and the repositories that feed it. In the catalog, filter entities by annotation through the API or the catalog's search, and in source control, run a code search across the descriptor files for each old key, including the catalog-info.yaml files that live in component repositories and any templates that generate them.

Removing a key is a change to every descriptor, so plan it as a batch rather than a sweep. Decide whether the replacement key needs a value that differs from the old one, because a rename is not always a straight swap, and test one entity end to end before you change the rest. Keep the catalog read-only or in review for the batch if your organization manages it through Git, and have the owning teams approve their own files. Add the search to your routine checks so that a template or a plugin upgrade does not reintroduce the old key. Ask your partner or internal team to show the search query and the list of affected files as part of the handover.

Security risks in annotation-driven links

Well-known annotations are plain text in descriptors, so anyone who can change a catalog file can change where a tool link points and which credentials a plugin uses. The cards below cover the risks that follow from that, and the controls you can confirm in your own repositories and Backstage configuration.

Unreviewed descriptor changes

Annotations such as github.com/project-slug or sonarqube.org/project-key are set by whoever can edit a catalog-info.yaml file. A change that points an entity at another team's repository or quality project can mislead dashboards and tooling that trust the link. Confirm that catalog descriptors sit behind branch protection with required reviewers, and that the catalog location is read from a branch your platform team controls.

Secret paths revealing structure

Vault and secret-path annotations name where credentials live, which can expose internal naming for teams, environments and services to anyone who can read the catalog. The secret value itself stays in Vault, but the path is still information an attacker can use. Check who can read catalog entities in your Backstage authorization setup and whether path names follow a convention that avoids exposing sensitive system details.

Plugin credentials set the reach

A plugin that reads an external tool through a service account can show data that the signed-in user could not reach directly in that tool. The annotation only selects which resource to query, so the effective permission is the plugin's credential scope. Confirm which token each integration uses, grant it read-only scope where the plugin allows, and review who holds rights to change the token.

Links to unowned systems

When a team moves on or an entity is left behind, its annotations can still point to live build pipelines, dashboards and projects. Those links may keep exposing data or accepting actions long after anyone maintains them. Review entities that have no current owner, and confirm that orphaned entities are removed or reassigned through your catalog's orphan handling rather than left in place.

Annotation values in other systems

Some plugins pass annotation values into URLs or queries against external APIs. A malformed or hostile value can send requests to unexpected hosts or inject query parameters. Confirm the input validation in the plugin you use, and add a catalog processor or schema check that rejects values outside the expected format before entities are registered.

Deprecated annotations left in place

Old annotation names may keep working through fallbacks while no longer being maintained, so a stale value can silently route data to the wrong place. Inventory the annotations your catalog uses against the current plugin documentation, and remove deprecated keys in the same change that migrates the entity. Track the migration in your change records so reviewers can verify it.

Questions teams ask about Backstage annotations

Do Backstage annotations require code changes?

No. Annotations are keys you add under metadata.annotations in a catalog-info.yaml descriptor, so the change lives in the repository that owns the component. The catch is that each annotation only does something when the matching plugin is installed and configured in the Backstage app. Adding github.com/project-slug to an entity without the GitHub plugin does nothing visible. Plan the plugin work and the descriptor work together, and confirm the exact key names and plugin setup for your Backstage version in the plugin documentation before you roll them out.

How much effort does a standard annotation set take, and what drives it?

The effort depends on four things: the number of entities that need annotations, the number of tools you want to link, how good your existing descriptors already are, and how much governance you put around changes. Entities with clean names, owners and lifecycle values need only a handful of annotation edits each. Entities with inconsistent names or missing ownership need cleanup first. Each tool adds its own configuration, credentials and testing. Ask any partner for a quote that breaks the work down by tool and by entity group, and separates one-time plugin setup from the ongoing edits your teams make to descriptors.

Can our internal staff set up well-known annotations on their own?

Often yes, if you have engineers who can edit Backstage configuration, manage the app's plugin packages and work with the tools involved. The hardest parts are usually not the YAML. They are getting admin access and credentials for GitHub, Jenkins, SonarQube or Vault, deciding the naming convention for annotation values, and testing that each link resolves to the right project. A useful first step is a pilot on a small group of entities with one tool. That shows you where your own team needs help and gives you a clear scope to compare against a partner's written proposal.

How do annotations change during a Backstage upgrade?

Descriptor keys themselves are plain metadata, so they usually survive an upgrade unchanged. What changes is the plugin behind each key. Plugins are versioned with the rest of the @backstage packages, and an upgrade can rename a key, change the expected value format or deprecate an annotation in favor of another. Check the deprecation notes in each release and the Backstage Upgrade Helper before moving versions. Keep a test entity for each annotation so you can confirm that links, CI status and scan results still appear after the upgrade. For the upgrade mechanics, see the keeping Backstage updated guide.

Which deprecated annotations should we clean up first?

Start with any annotation that your current Backstage version marks as deprecated, and any key your plugin list no longer uses. Search your descriptors for keys that no installed plugin reads, because those are dead weight that confuses owners. Then replace deprecated keys with their current equivalents, one tool at a time, and verify each link after the change. Keep a record of which keys you removed so that a future plugin install does not reintroduce them by copying old templates. Confirm the current deprecation list against the Backstage documentation for your version, since the list changes over time.

What do we need to provide for annotation work?

You need three kinds of access and time from your side. Plugin owners need to agree on the annotation naming and on which plugins the portal will run. Tool administrators need to create or approve the integration accounts, tokens and project mappings that each plugin uses, with the least privilege the plugin supports. Repository owners need to let the work land in their catalog-info.yaml files, usually through pull requests they review. Name a single owner for the descriptor standard, because annotation drift is the most common reason a catalog stops being useful. Ask any partner to list these inputs in the written scope so you can schedule them before work starts.

Who should own annotation values once the catalog is live?

The team that owns the component should own its annotation values, because they know which repository, pipeline and scanner project belong to it. The platform or portal team should own the standard: which keys are allowed, what format each value uses and how the checks run. Put validation in the pipeline that registers descriptors, so a missing or malformed annotation fails early rather than showing up as a broken tab in the portal. Review ownership when a team reorganizes, since stale annotations pointing at old projects are easy to miss and can send incident responders to the wrong dashboard.

How DeployOpen scopes annotation standards and rollouts

DeployOpen scopes annotation work as a fixed-scope engagement, and the first deliverable is the annotation standard itself: which annotations each entity kind must carry, which value formats are allowed, and who owns each value. That standard is agreed with the people who run the tools being linked, such as the source control, CI, code quality and secrets teams, because they decide what a valid github.com/project-slug, Jenkins job name or SonarQube project key looks like in their systems. The working session also settles which annotations are required and which are optional, and where the catalog stops and the tool begins. Those decisions are written down before any descriptor is changed, so the later rollout has a reference to test against.

The written scope then defines a pilot, usually a small set of components from one or two teams, chosen so that each linked plugin is exercised at least once. The pilot shows which annotations the Backstage instance already renders, which need plugin configuration first, and which produce empty cards because the source system does not hold the expected data. The statement of work lists the entity kinds in scope, the plugins and their versions, the validation checks (for example, processors that reject malformed values), the deliverables such as the standard document, the descriptor changes and a rollout runbook, and the acceptance criteria. Confirm with the vendor or your own environment which plugin versions your Backstage release supports before you commit to them in the scope.

After the pilot, rollout can be handled by your own team, by DeployOpen as part of the original scope, or under a retainer that covers annotation changes and the plugin updates that follow each Backstage upgrade. Ask any partner to show how they will track deprecated annotations and remove them in a planned change, rather than leaving old keys in place. Quotes should separate the one-time work (standard, pilot, rollout) from recurring work (plugin upgrades, new tool onboarding, periodic catalog checks), and state which party provides credentials, test projects and sign-off.

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.