Building a custom Backstage entity provider

A custom entity provider lets your Backstage catalog ingest entities from an internal inventory or API that no built-in provider reads. This page covers the design, mutation modes, scheduling and deletion behavior you must plan before writing code.

On this page

When a custom Backstage entity provider is the right fit

A custom entity provider is justified when your service records live in an internal inventory or API that no built-in provider reads, and copying them into Git would create a second source of truth you do not want to maintain. In Backstage, an entity provider is the component that brings entities into the Software Catalog from an external source. It connects to the source, reads records, converts them into entities and hands them to the catalog for processing. Before writing one, check the simpler options. A static location registered with a catalog-info.yaml file is enough for a few repositories that teams already own. A discovery provider that ships with Backstage is the better choice when your source is a git platform or directory that the project already supports. Processors do not fit this need at all, because they validate and enrich entities that already exist and cannot bring new ones in. Confirm the current list of built-in providers against the Backstage architecture overview and the catalog documentation for your release, since the set changes between versions.

Justification usually rests on three questions. First, does the source hold the authoritative record, such as a CMDB, an asset register or a service registry that already drives change approval? If so, the source should stay the system of record and the catalog should mirror it. Second, can the source be queried in a way that tells you what changed, or will every run need the full list? Third, who will own the provider code after launch, and how often will the source's schema change? An illustrative case: a platform team has a service inventory API with about a few thousand records, updated by a change process. Static files would need every team to edit two places, and a discovery provider does not read that API. A custom provider fits because it reads the API on a schedule and keeps the catalog aligned with it. If your inventory has a small number of records that teams rarely change, a static location plus a validation step may be cheaper to run and easier to audit. Plan the ownership question before the build starts. Whoever maintains the provider must handle authentication to the source, API version changes, pagination and error handling, and upgrades of the Backstage packages it depends on. Ask your internal API owner for a read-only service account, a documented rate limit and a way to list changed records since a given time. Those answers decide whether the provider will use full or delta mutations, which the next section covers, and they set how much code your team will carry after handover.

Full versus delta mutations: which one to write

A full mutation tells the catalog that the complete set of entities from this provider is now exactly the list in the payload. On each run the provider reads the whole source, builds the full list and hands it over. Anything the provider previously emitted that is missing from the new list is removed by the catalog, which is what makes full mutations simple to reason about. The cost grows with the source. Every run reads every record and rewrites every entity, even when nothing changed, so a source with many thousands of records spends most of its time on unchanged data.

  • Simple correctness: the payload is the source of truth, so the catalog state always matches the last successful read.
  • Cost on large sources: reading and processing the full set on every interval grows with the source, not with the number of changes.
  • Partial responses are dangerous: if the source returns a page of results and the provider treats it as complete, the missing entities are scheduled for removal.
  • Deletion is implicit: removed records disappear from the catalog on the next successful full run, without any explicit delete instruction.
  • Good fit: smaller sources, or sources where the provider cannot tell which records changed.

Before you choose full mutations, confirm how your source paginates and whether a failed or truncated read can be told apart from a genuinely short list.

Configuration options and when each fits

This table compares the decisions a builder makes when writing a custom entity provider: mutation mode, schedule, provider identity, source linkage annotations and orphan strategy. Read each row as a question to settle before the first run, then check the defaults in the Backstage version you run. The main takeaway is that the mutation mode and orphan strategy together decide what happens when a record disappears from the source.

DecisionWhen it fitsTrade-off
Full mutationThe source is small enough to list completely on each run, or it has no reliable change feedEvery run sends the whole set, so runtime and load grow with inventory size
Delta mutationThe source reports additions and removals, and the inventory is large or changes oftenYour code must track what it already sent, and a missed delta can leave stale entities until a full run
Schedule frequencyFrequent runs suit data that teams change often; slower runs suit slow-moving inventoriesShort intervals raise load on the source and on the catalog; confirm the scheduler defaults in your version
Task timeoutSet it above the slowest realistic run so a healthy run is not cut shortA long timeout delays detection of a hung provider; a short one can abort large full runs
Provider identity and namingUse a stable, descriptive name per source so the catalog can tell which provider owns each entityRenaming a provider looks like removing one, which can trigger the orphan strategy for its entities
Source linkage annotationsAdd an annotation that records the source system and record ID on every entity the provider createsTeams must keep the annotation accurate; missing values make it hard to trace or clean up entities
Orphan strategy: deleteThe source is the single authority and removed records should disappear from the catalogA temporary source outage or a bad export can remove many entities at once
Orphan strategy: keepRemoved records should stay visible for review, or the source is not complete enough to trustStale entities accumulate and can mislead owners and plugins until someone removes them

Build sequence for a custom entity provider

This sequence takes a custom entity provider from an agreed source contract to a tested, scheduled integration that creates, updates and deletes catalog entities as the source changes. Each step closes with a check you can run before moving on.

  1. Define the source contract and unique keys

    Document what the internal inventory or API exposes: the record fields, how records are listed, whether changes can be fetched incrementally, and which field uniquely and permanently identifies each record. Confirm the key is stable across renames and moves, since the catalog uses it to decide whether an entity is the same one on the next run. Owners of the source system and the Backstage team should sign off on this document together. The check: every record in a sample export has a unique key, and two exports taken a day apart produce identical keys for unchanged records.

  2. Map source records to catalog descriptors

    Write the mapping from each source record to a catalog entity, covering apiVersion, kind, metadata.name, metadata.namespace, lifecycle, and spec fields such as owner and system. Decide how the source key and any source-specific details are carried, typically as annotations, so the entity can be traced back to its record. Validate sample mapped output against the descriptor format before writing any provider code, and confirm that metadata.name values are unique within the namespace. The check: a set of representative records maps to entities that pass descriptor validation with no warnings about missing owners or invalid lifecycle values.

  3. Implement the provider with connect and run

    Build the provider class against the Backstage catalog backend interfaces. The connect method receives the entity provider connection and stores it, and the run method fetches records from the source and sends them to the catalog as a full mutation or a delta mutation. Keep source credentials in configuration and secrets, not in code. Developers should implement the full mutation first, because it defines the complete set of entities the provider owns and is the simplest correct behavior. The check: a local run against a test source produces the expected entity count in the catalog, and a second run with no source changes produces no entity changes.

  4. Register the provider and set its schedule

    Add the provider to the catalog backend, give it a unique name, and set its schedule in configuration, including the frequency and timeout values supported by your Backstage version. Confirm which catalog location and provider name the entities will carry, because the orphan handling settings refer to that provider name later. Restart the backend and confirm the provider appears in the catalog's provider list with a successful last run. The check: the provider reports a completed run in logs or the catalog backend status, and its entities appear with the expected provider-derived origin.

  5. Test full and delta runs against staging

    Point the provider at a staging copy of the source and exercise three cases: an initial full load, a run after adding records, and a run after changing record fields. If you implement delta mutations, test that added, modified and removed records produce the matching additions, updates and removals, and that unchanged records are untouched. Compare the catalog contents with the source after each run. Ask the source owner to confirm that the staging data reflects production shapes, including missing optional fields and unusual characters. The check: after each run, catalog entities match the source record set exactly for the fields you map.

  6. Verify that removed records delete their entities

    Remove a test record from staging and run the provider again. Confirm the matching entity is removed from the catalog, and check how the orphan handling settings for this provider affect the result, since an entity can be deleted or kept depending on the strategy you configured. Also confirm that an entity an operator deleted by hand does not return unless its source record still exists and the provider still emits it. Record the chosen behavior in your runbook. The check: a removed source record leads to the expected entity deletion on the next run, and the result matches the documented strategy.

  7. Confirm behavior with the source owner before production

    Before enabling the schedule in production, walk the source owner and the catalog owners through the test results, the deletion behavior and the failure mode when the source is unavailable. Confirm with your Backstage version's documentation how a failed run is reported and whether a partial result can remove entities, and make sure the provider does not treat an empty or error response as a valid empty inventory. The check: a documented sign-off that the source contract, mapping, schedule and deletion behavior match what the owners expect, with the failure path tested in staging.

How removed source records lead to entity deletion

A record that disappears from the source leaves the catalog only when your provider stops emitting it. The catalog does not watch the source directly. It compares what the provider reported in its last successful run with what it reports now, marks entities that are no longer emitted as orphans, and then applies the strategy you configured. In a full mutation, the provider sends its complete set of entities for that provider, so any entity missing from the list is treated as removed. In a delta mutation, the provider names the removed entity references explicitly, so only those entities are affected. Full runs are simpler to get right, while delta runs depend on the provider tracking changes reliably between runs. Strategy details are covered on the orphan handling page.

The risk that matters most is a partial or failed response being read as a mass deletion. A full mutation built from an empty or truncated list will mark every entity the provider owns as removed. A common cause is pagination: a run that stops after the first page looks the same as a run against a source that is genuinely empty. Make the provider fail loudly instead. Let errors surface from the run so the mutation is not applied, compare the returned count with the previous run, and treat an unexpectedly large drop as a stop condition rather than a sync. Confirm how your Backstage version handles an error thrown during a scheduled provider run before you rely on that behavior.

Delta runs carry a different risk: a removal that is never reported. If the change feed drops events, or a record is deleted and recreated under the same identifier between runs, the catalog keeps an entity the source no longer describes. Plan a periodic full reconciliation to correct drift. Before go-live, test the removal path against a copy of the source: delete a test record, run the provider, and confirm the entity moves to orphaned and then follows the configured strategy. Ask the team that owns the inventory how deletions are recorded, and whether soft-deleted records should be emitted as removals or left out of the output.

Security and operational risks to design out

A custom entity provider runs unattended, reads from a system you may not fully control, and writes directly into the catalog that teams use to decide who owns what. The cards below cover the controls that keep a provider from leaking credentials, over-reaching into the source, or deleting large parts of the catalog after a bad run.

Keep source credentials out of code

The provider needs a token or client secret to read the inventory. Load it from the backend secret configuration or your secret manager, never from the provider source or a committed app-config.yaml. Use a separate secret per environment so a staging run cannot read production data. Confirm who can rotate the credential and how a rotation reaches running backends without a code change.

Grant read-only, scoped access

Create a service identity in the inventory system that can only list and read the record types the provider maps. Avoid admin scopes, write permissions and access to unrelated projects or tenants. Document the exact scopes in the provider README so a security review can compare them with what the code calls. Recheck scopes after each change to the field mapping.

Validate fields before they become names

Inventory fields such as service names, owner emails and tags flow into metadata.name, annotations and relations. Reject or normalize values that break entity naming rules, contain unexpected characters, or claim an owner outside your group model. Treat the source as untrusted input. A malformed name that collides with an existing entity can overwrite or shadow a record owned by another team.

Guard against a run that empties the catalog

A timeout, an empty API page or a changed response shape can make the provider report zero records. Under a full mutation, the missing entities are then removed. Add a minimum-count check or a maximum-deletion threshold, and abort the run and alert when it trips. Confirm how orphan strategy settings in your deployment handle a provider that stops reporting, and test that path before production use.

Log and monitor every run

Record the start and end time, the number of entities added, changed and removed, the count of rejected records and the error count for each run. Send these to your logging platform and alert on failed runs, sudden drops in entity count and runs that do not complete on schedule. Without these signals, a provider that silently stopped working looks like a catalog that is simply up to date.

Assign owners for provider upgrades

The provider depends on Backstage catalog APIs and on the inventory's API version, and both change over time. Name a team that owns the provider code, reviews it on each Backstage upgrade, and tests it against a copy of the source before the upgrade reaches production. Ask who will maintain the code after the original author moves on, and keep the provider in your own repository with tests.

Questions teams ask before building a custom provider

How much effort does a custom Backstage entity provider take?

Effort depends mostly on how the source exposes its records and how many fields must be mapped into entities. A provider that reads one paginated internal API with stable identifiers and a simple mapping is a small, contained piece of code. Effort rises with each additional concern: authentication to the source, pagination and rate limits, mapping several entity kinds, handling partial failures, and writing tests against realistic data. Ask your implementation partner to break a quote into discovery of the source, the provider code, scheduling and deletion behavior, tests, and handover. Build cost is largely one-time, while the cost of keeping the provider working recurs through Backstage upgrades and changes in the source.

Can an internal team build a custom provider without outside help?

Yes, if the team has engineers comfortable with TypeScript, the Backstage plugin model and the catalog's entity format. The harder part is usually not the code but the decisions around it: which records become entities, what counts as a removal in the source, and how full and delta mutations are used so the catalog stays correct. Teams that build it themselves often benefit from a short design review before coding and a test run against a copy of production-like data. An outside partner can supply that review, the test plan and a handover document, which is useful when the engineers who wrote the provider move on.

What does the work need from the source owner?

The source owner needs to confirm three things before the build starts: how records are identified, whether the source can report changes since a point in time, and how deleted or retired records appear. Without a stable identifier, the provider cannot reliably match records to entities across runs. Without a way to detect removals, deletions in the source will not reach the catalog. Ask the owner for read credentials scoped to the minimum needed, a test environment or a sample export, and a named contact for schema changes. Agree in writing who approves changes to the field mapping, because those changes alter what teams see in the portal.

How long does a first version take?

The timeline depends on the source. A source with a documented, paginated API and clean identifiers is a different job from a legacy inventory that exports nightly files with inconsistent keys. Ask for a time estimate only after the source has been examined, and ask the provider to name the assumptions behind it, such as the number of entity kinds, the number of source systems and whether a test environment is available. A fixed-scope written agreement should list the deliverables and the acceptance tests, so both sides can see when the first version is done.

Does a custom provider need changes when Backstage is upgraded?

It might. A provider depends on Backstage packages such as the catalog backend and the plugin APIs, and those packages change across releases. Backstage moves all @backstage packages together, typically with yarn backstage-cli versions:bump, and the Backstage Upgrade Helper shows template changes between versions. Your custom provider should be built into that same upgrade cycle, with its own tests run after each bump. Keep the provider small and free of internal framework details so upgrades stay routine. Plan for this recurring maintenance when comparing options, because a provider that nobody maintains will eventually stop matching the catalog API.

When is a discovery provider or processor a better choice?

Use a built-in discovery provider when the source is a Git platform or directory that Backstage already reads, because it handles scheduling and deletion for you. Use a processor when you need to validate, enrich or add relations to entities that already exist in the catalog, since processors can change entities but cannot delete them. A custom entity provider fits when records live in an internal inventory or API that no built-in provider reads. Compare the three choices against where the source of truth lives, who needs to change the data, and how deletions should flow. Your partner can confirm which pattern fits your sources before any code is written.

Who is responsible for deleting entities when a source record disappears?

The provider is responsible, through the mutations it reports to the catalog. When a full mutation lists the current set of entities, anything from that provider not in the list becomes a candidate for removal. Delta mutations report additions and removals explicitly, which suits large sources but requires that removals are detected correctly. Test deletion behavior deliberately, including what happens when the source is temporarily unavailable, because an empty or failed response must not be mistaken for a full set of removals. Agree the orphan and deletion policy with the catalog owners before the provider goes live.

How DeployOpen works on this

DeployOpen scopes a custom Backstage entity provider in writing before any code is written, then builds it inside your own infrastructure and source control. The engagement ends with a provider your team can run, test and extend, along with the documentation needed to operate it after handover.

How we scope

  • We confirm the internal inventory or API the provider reads, its access method, and how records are identified.
  • We agree in writing whether the provider runs full or delta mutations and how often it is scheduled.
  • We define how removed source records map to entity deletion, with the expected behavior for orphans documented.

How we build

  • We implement the provider against the Backstage backend, with credentials held in your secrets store rather than in code.
  • We test full refresh, delta updates and source record removal against a staging copy of your inventory.
  • We validate generated entity descriptors with your processors and confirm the resulting catalog graph matches the agreed model.

How we hand over

  • You receive the provider source, configuration and tests in your repository, with the build steps documented.
  • We document schedules, mutation behavior, deletion rules and the runbook for diagnosing ingestion failures.
  • We walk your platform team through a working deployment so they can change the provider and rerun the tests themselves.

How we support

  • Engagements are fixed-scope, and any follow-up work is quoted from a new written scope.
  • Ongoing support, Backstage version upgrades and continuous testing are available as a retainer.
  • Confirm with your team how the provider will be re-tested when Backstage packages are bumped to a new release.

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.