When teams need a custom entity kind in Backstage
A custom entity kind is justified only when the thing a platform team wants in the catalog has its own identity, lifecycle and relations that teams need to query, and none of the default kinds describes it without distortion. In most cases a Component, a Resource or a System with a few annotations and a spec.type value is enough, and that is the cheaper path. Treat a new kind as a platform commitment that every plugin, template and upgrade will have to respect.
In the catalog descriptor, kind is a top-level field next to apiVersion and metadata, and it tells the catalog which schema the entity follows. The software catalog ships with built-in kinds such as Component, API, Resource, System, Domain, Group, User, Location and Template. The architecture overview describes the Software Catalog as a core built-in feature, so these kinds are the starting vocabulary that the portal's pages, relations and plugins already understand. Confirm the exact list, the required spec fields and any kind-specific behavior against the catalog documentation for the Backstage release you run, because kind support has changed between releases.
Before adding a kind, write down the questions the catalog must answer about the new thing: who owns it, what it depends on, which lifecycle stage it is in, and which plugin will read it. If a Resource with a spec.type or a System with extra annotations answers those questions, a custom kind adds nothing except maintenance. A new kind usually means changes in three places: the catalog rules that allow the kind to be ingested, the processors or validation that check its fields, and the frontend views and plugins that display or act on it. Each of these must be maintained through every Backstage upgrade, since the @backstage packages move together and custom code must keep pace with them.
The practical test is to build a sample descriptor for a few real records, register it in a non-production instance, and check whether the catalog relations and existing plugins show what your teams need. If they do not, ask whether a processor or an entity provider is the better place for the logic, and confirm how the chosen design interacts with validation and deletion behavior in your release.
Deciding between a custom kind, a Component type or a Resource type
Before adding a kind to the catalog, test whether the thing needs its own lifecycle and owner, whether existing relations and plugins already express it, and whether a type value on Component or Resource is enough. Each answer points toward a cheaper model or toward a custom kind that your teams must maintain.
Does it have its own lifecycle and owner?
Ask whether the thing is created, changed and retired on its own schedule, and whether a named team answers for it separately from the component that uses it. If it shares the lifecycle and owner of a Component, a Component with a type value usually fits. If it needs independent versioning, approvals and deprecation, a distinct kind becomes easier to justify. Check this with the team that would be paged when it breaks, since their answer usually settles the ownership question.
Can relations and plugins already express it?
Look at what the catalog graph and the plugins you plan to install already model. The built-in relations on Component, Resource and System may describe dependencies, ownership and hosting well enough. If a plugin reads a specific kind to show a view, a custom kind may be required. If no plugin reads the kind, a Resource with a clear type is often the simpler choice.
Is a spec.type value enough?
A spec.type value on a Component or Resource can separate services, websites or libraries without changing the kind. Test whether the difference changes validation rules, relations or the entity page layout. If it only changes a label or a filter, the type field is the lower-cost option. If the variants need different required fields that a processor must enforce, record that requirement before choosing a new kind.
Will other teams query it as a category?
Consider whether other teams will search, filter or report on this thing as a category in its own right, such as all API keys or all data pipelines. A distinct category answers that query directly. If the question is only a subset of an existing kind, a type filter or tag may serve better. Ask the consuming teams to name the queries they expect to run, because a kind that nobody queries adds maintenance without adding value.
Who maintains the kind after launch?
A custom kind must be allowed in catalog rules, validated by processors and understood by every plugin that reads it. Each Backstage upgrade may touch those pieces, so the kind carries recurring work that a type value does not. Estimate that cost with your platform team before the first entity is registered, and confirm with your team how the kind will be reviewed when the portal version changes.
Options for modeling a new thing in the catalog
This table compares four ways to represent something new in the Backstage catalog: a Component or Resource with a spec.type value, a custom kind, or an annotation or relation on an existing entity. Read each row as a question you must answer before writing a descriptor, starting with whether the catalog rules and plugins already know how to handle the shape. The main takeaway is that a custom kind is the most expensive option to maintain, so it should be chosen only when the existing kinds cannot express the thing you need to track.
| Option | When it fits | What catalog rules and plugins need | Main trade-off |
|---|---|---|---|
| Component with spec.type | A deployable or runnable software unit, such as a service, website or library, that differs only by type | Nothing new. Catalog views, ownership and well-known annotations already apply to Components | Type values are free text, so teams can drift without shared validation |
| Resource with spec.type | Infrastructure a Component depends on, such as a database, bucket or queue | Dependency relations from Components, and a Resource entry in the catalog | Resources carry less metadata by design, so rich attributes may need annotations |
| Custom kind | A thing with its own lifecycle and fields that neither Component nor Resource describes, such as a data product or regulated asset | Allowing the kind in catalog rules, a processor or schema to validate it, and UI or plugin code that understands its fields | Every plugin that reads the kind, and every upgrade, must be checked against it |
| Annotation on an existing entity | A small attribute or link that only one plugin or report needs | An annotation key documented for teams, and a plugin that reads it | Annotations are not queryable as first-class objects, so they cannot hold their own owner or lifecycle |
| Relation on an existing entity | A link between two entities that already exist, such as a Component that owns a Resource | Relation generation through processors or the descriptor, and views that display the link | Relations connect entities but do not create a new entity to inventory or own |
| Separate system outside the catalog | Records that change far faster than the catalog or belong to another tool of record | An API or plugin that reads the other system and links back to catalog entities | Two sources of truth need a clear owner, and search will not index the external records |
| Validate before choosing a custom kind | Any proposal, to confirm the kind is needed | Confirm with plugin owners which installed plugins read the kind | Skipping this step is the most common reason custom kinds become hard to keep |
How catalog rules decide which kinds are accepted
The catalog accepts only the entity kinds its rules allow, so a custom kind must be permitted in configuration before any descriptor using it is ingested. In Backstage, this permission lives in the catalog rules section of the app configuration, where the catalog.rules list names each allowed kind (and optionally the locations that may register it). Until the kind is listed there, a catalog-info.yaml declaring it has no valid path into the catalog, regardless of how well the descriptor is written. Confirm the exact rule syntax against the catalog documentation for the Backstage version you run, because the configuration shape has changed over time.
When a kind is missing from the rules, the symptom is usually not a clear error in the UI. Entities may be rejected during processing and recorded as errors on the location that registered them, or they simply never appear in the catalog. Start by checking the processing errors for the location in the catalog, then compare the kind in the descriptor against the rules that the running backend actually loaded. Check the configuration each environment uses, not only the file in your repository, since a value set through an environment-specific override or a deployed secret can disagree with what you expect.
Treat rule changes as code. Keep the catalog.rules entries in version-controlled configuration, review them like any other change to the portal, and deploy them through the same pipeline as the backend. Before merging, test the change against real descriptors from your repositories, including at least one per custom kind and one that should still be rejected, so you know the rule admits what you intend and nothing broader. A rule that is correct for one sample file can still block a descriptor that uses a different apiVersion or location, so use representative files drawn from your estate.
Adding a custom kind to a Backstage catalog
This sequence takes a proposed custom kind from a written model to a tested catalog entity in a non-production instance, with a validation check at each stage so problems surface before the kind reaches production.
Agree the model and owners
Before writing any YAML, the platform team and the teams who will register these entities agree on what the kind represents, which fields are required, who owns the kind definition, and who approves changes to it. Record the decision in a short design note that lists the kind name, the spec fields, the relations to Components, Systems and Resources, and the named owners. This step is done when the design note has a named owner for the kind and a reviewer from each team that will register entities. If the same information fits in a Component or Resource with a type value, stop here and record that decision instead.
Write the descriptor schema and sample files
Draft the descriptor for the new kind, with apiVersion, kind, metadata and spec fields matching the design note, then write two or three sample catalog-info.yaml files that represent realistic cases, including one with an optional field left out. Keep the samples in a repository that the non-production instance can read. This step is done when every sample parses as valid YAML and matches the field list in the design note, which you confirm by reviewing the files against that list before any ingestion attempt.
Allow the kind in catalog rules
By default, the catalog only accepts kinds listed in its rules, so add the new kind to the catalog rules configuration for the non-production instance. Confirm the rule scope in the Backstage catalog documentation for your version before editing, because the configuration keys and their location have changed across releases. This step is done when the catalog starts without configuration errors and the new kind is accepted rather than rejected at ingestion. Record the exact config change in the same repository as the descriptor so reviewers can see both together.
Add a processor for required fields and naming
Write a catalog processor that checks the new kind's required fields, naming pattern and allowed values, and reports errors for entities that break those rules. Register the processor with the catalog in the non-production instance and keep its logic narrow, since processors validate and enrich entities but do not delete them. This step is done when a deliberately bad file, such as one with a missing owner or a name that breaks the pattern, produces a visible validation error in the catalog while the valid samples are ingested without errors.
Ingest sample entities in a non-production instance
Register the valid sample files through the catalog location or provider you already use for non-production testing, then trigger a refresh and confirm the entities appear with the expected kind and spec values. Test one update to a sample file and one removal so you can see how the catalog handles changes and deletions for this kind. This step is done when the valid entities are present after refresh, the update is reflected, and the removal produces the behavior your design note expects.
Check entity pages and relations
Open each sample entity in the catalog UI and check that the kind label, spec fields, owner link and relation cards render as the design note describes. Follow each relation to the linked Component, System or Resource and confirm it resolves to an existing entity rather than a placeholder. This step is done when every sample entity renders without missing fields, all relations resolve, and the reviewers from each team have signed off on the page layout and the relation graph. Anything that fails here returns to the schema or processor step.
Confirm plugin and upgrade impact before production
Before promoting the kind, list every plugin that reads or displays catalog entities and check whether each one handles the new kind or needs custom code. Ask the team that owns each plugin to confirm support, and record the upgrade dependency in the design note, since custom kinds must be kept compatible with each Backstage upgrade. This step is done when each consuming plugin has a documented status, and the team has agreed who maintains the kind definition through future upgrades.
Validating custom kinds with catalog processors
Processors are the place where a custom kind gets its rules. A catalog processor receives each entity after a provider or location has read it and can check it, reject it, or add annotations and relations before it is stored. For a custom kind, this is where you enforce naming conventions, required spec fields, allowed values for lifecycle-style properties, and the relations a valid entity must carry. Confirm the processor interface and the hook names for your Backstage version before writing code, because the processor API has changed across releases and a processor written against one line may need rework on the next. Keep the rules few and explicit so that a failed check tells the author exactly which field to fix.
Processing order matters. Processors run in a sequence set by their registration and priority, so a validation rule that depends on an annotation added by another processor will fail or pass depending on where it sits. Write each rule to depend only on the raw descriptor where possible, and test the order with a sample file that exercises both your validator and any enrichment processors. Be aware that a processor cannot delete an entity. It can only mark a problem, and the entity may still be ingested or left in place. If a rule must remove or stop an entity, that logic belongs in an entity provider, which controls what is emitted from the source. The same applies when the check depends on data outside the descriptor, such as a lookup in an internal inventory, because providers are the component designed to fetch and reconcile that data on a schedule.
Before relying on processors for a custom kind, confirm three things with your own environment: which processors your deployment loads, whether validation failures block registration or only surface as errors in the catalog, and how your team will see those errors. Ask the platform team to publish one valid and one invalid example per kind, and run both through the catalog in a non-production instance. Processor logic is also code you must maintain through upgrades, so budget its testing alongside the kind's schema. Anything that cannot be expressed as a deterministic check on the entity should move to a provider or to a review step outside the catalog.
What a custom kind costs plugins and integrations
A custom kind is only useful if the tools around the catalog understand it. Each cost below lands on a different team, and each one grows with every plugin, filter and upgrade that touches the new kind.
Plugins that only know built-in kinds
Many community and vendor plugins query the catalog for Component, API, Resource or System and ignore anything else. Your custom kind will not appear in their views until someone adds it to their filters or writes a wrapper. The platform team pays for this when a plugin silently shows nothing, so before adoption list every plugin in your portal and confirm how each one filters by kind. Where a plugin cannot be configured, plan the custom code yourself and budget it as ongoing work.
Entity page cards and tabs
Entity pages choose their cards by kind, so a new kind gets a blank or generic page until you wire in an entity page for it. Someone on the frontend team writes and tests that routing, and every new card needs the same attention. Ask which team owns these pages, and whether the cards you need already exist for a built-in kind that you could reuse with a configuration change.
Search, filters and facets
Search results and catalog filters rely on the kind field and its values. A custom kind has to be indexed and exposed in the catalog filters before users can find entities of that kind by browsing. Check the search configuration for each kind you add, and test with real entities that users would look for, since a kind that never appears in results is effectively hidden.
Custom frontend code maintenance
Any card, page or filter you write for a custom kind is code your team must keep compiling and working. Each Backstage upgrade can change the frontend APIs those components use, so this work recurs with every release line you follow. Name a maintainer, record which packages the code depends on, and include the custom frontend in the upgrade test plan rather than treating it as a one-time build.
Templates, relations and integrations
Software templates, relations and integration annotations may expect built-in kinds as inputs or targets. A custom kind can break a template that outputs it or a relation that points at it. Before adding the kind, trace which templates, processors and plugins reference the kinds you are changing, and test each path end to end in a staging portal.
Upgrade and skills ownership
Custom kinds add a support burden that falls on whoever knows the schema and the validation rules, often one or two engineers. When they leave, the kind becomes hard to change and plugin authors may stop updating for it. Ask who will maintain the schema, the processor and the frontend code after the first release, and whether the team can absorb that work during a Backstage version change.
Questions teams ask before adding a custom kind
Is a Component type enough before I add a custom kind?
Often yes. A Component with a well-chosen spec.type and annotations can represent most services, libraries and tools, and it works with the existing catalog pages, relations and plugins without extra code. Add a custom kind only when the thing you need to track has a lifecycle, owner model or set of fields that does not fit Component, Resource or System without distorting them. Before deciding, write down which plugins must read the entity and which fields they need, because a custom kind is only useful if something consumes it.
What drives the effort of adding a custom entity kind?
The main drivers are the number of fields, how many plugins need to understand the kind, and how much validation the catalog must enforce. A kind with a small schema and one consumer is a modest change. Effort rises with each of these: a JSON Schema or processor that enforces naming and required fields, catalog rules that allow the kind to be ingested, entity pages or cards that render its data, and tests that prove a bad descriptor is rejected. Ask any partner to size these four items separately in the written scope.
Can our own team add a custom kind without a partner?
Yes, if you have engineers who know the Backstage catalog backend, TypeScript and the plugin build tooling. The catalog permits custom kinds through its rules configuration and processors, so the work is mostly design and testing. The risks are in ownership: someone must maintain the schema, approve changes, and keep the descriptor contract stable for teams that already register entities. Confirm internal skills in these areas before deciding, and compare that against the time your platform engineers can give to a feature that will need upkeep.
What does the work need from our people?
Three roles matter most. A schema owner decides the fields, allowed values and naming rules, and signs off changes. Plugin developers provide the entity views or backend logic that read the kind. Catalog administrators manage the catalog rules, ingestion sources and the list of allowed kinds in each environment. In a small team one person may hold two roles, but the schema owner should not be the only person who can change the descriptor contract. Agree these roles in writing before the build starts.
How do custom kinds affect Backstage upgrades?
Custom kinds add code you must keep working across releases. The Backstage versioning policy describes a monthly main release line and a weekly next line, so catalog APIs and plugin packages change on a regular cadence. Your kind's processors, entity views and tests depend on those packages, and each upgrade may require changes in the same code. Plan a test run against a current release for every upgrade, and check the Backstage Upgrade Helper before moving versions. Keep the kind's logic in a small, well-tested module so the upgrade cost stays visible.
How should I scope a partner quote for a custom kind?
Ask for a written scope that lists the kind's fields and validation rules, each plugin view or backend change, the catalog rules and ingestion sources affected, the test cases and the handover of documentation. Also ask who maintains the code after delivery and how upgrades are handled. DeployOpen works from a written scope, and the quote follows from it. Ask every bidder to state which items are one-time and which recur, such as upgrade work or support retainers, so the totals can be compared fairly.
What should I check before the first custom kind goes live?
Confirm that a valid descriptor is accepted, that an invalid one is rejected with a clear message, and that existing Component entities still ingest unchanged. Check that the kind appears in catalog rules only in the environments where it is intended, and that search and relations behave as expected. Review access to the schema so that changes go through version control and review. Finally, test one plugin view against the newest release line your team plans to run, since that shows how much upkeep the kind will need.
Security and governance for custom kinds
A custom kind is only as trustworthy as the people allowed to register it and the checks that run before an entity lands in the catalog. The cards below cover the controls that connect registration rights, validation, identity and version control so the new kind does not become a back door for unowned or malformed entities.
Limit who can register the kind
Decide which teams may create entities of the new kind before you allow it in catalog rules. Registration usually happens through Git locations or providers, so the practical question is which repositories and which automation accounts can add a file declaring the kind. Write this list down, then confirm that only those sources are configured to ingest it. Ask whether any plugin can create entities of the kind through the API, since that path skips repository review.
Validate entities before they register
Use catalog processors to reject malformed entities of the new kind, such as missing owners, names that break your naming rules or missing required annotations. Processors can add annotations and check values, but they cannot delete entities, so a failed check should block the change at review time. Test each rule against sample files in a pull request pipeline so bad descriptors are caught before they reach the catalog.
Require an owner that resolves
Every entity of the new kind should name an owner that exists as a Group or User in the catalog. An owner reference that points nowhere leaves the entity without a team to answer for it, and the entity can drift into orphan status. Check that owner values follow the same format your other kinds use, and confirm which team reviews entities that have no resolvable owner.
Align sign-in resolvers with group ownership
Group ownership only works when a signed-in person resolves to the same identity that appears in the catalog. The authentication guidance advises configuring sign-in resolvers so every provider maps to one identity, so a user who signs in with GitHub and with your directory should not appear as two users. Test this with real accounts from each provider and check that group membership, not only the username, controls who can edit the kind's entities.
Keep guest access out of production
Guest sign-in lets anyone reach the portal without a directory identity, which means the ownership and registration rules above can be bypassed by an unidentified user. The authentication guidance advises against offering guest access in production. Before go-live, confirm the guest provider is disabled in the production configuration and that a test sign-in as an unknown user is refused.
Keep the kind model in Git
Store the kind definition, its catalog rule and its processor configuration in the same repository as the rest of your Backstage configuration, and change them only through reviewed pull requests. This gives you a history of who allowed the kind and when its rules changed, which auditors and upgrade reviewers both need. Record which team approves changes to the model so a later plugin upgrade does not silently alter how entities of the kind are validated.

