How Backstage imports PuppetDB nodes as Resource entities
A platform team that runs Puppet-managed servers usually wants each service in the Backstage catalog to point at the hosts it runs on, so that an outage on a host is traceable to the services it affects. Backstage handles this with the PuppetDB catalog module, catalog-backend-module-puppetdb, which is part of the core Backstage repository. The module reads nodes from PuppetDB and creates Resource entities for them. Resource entities describe infrastructure a system needs in order to operate, and a Component can then declare spec.dependsOn to a host it relies on, while the host can point back with spec.dependencyOf. The result is a graph that links services to servers without anyone maintaining those links by hand.
This page covers only the supported method, the catalog module reading from PuppetDB. It does not describe scraping Puppet manifests, copying host lists into catalog files, or other workarounds. Before configuring anything, confirm which PuppetDB endpoint your fleet reports to, which certificates the Backstage backend will use to reach it, and how node names in PuppetDB map to the hostnames your services already use in their descriptors. Mismatched names are the most common reason a dependency fails to resolve, so compare a few real node names against the service descriptors first.
OpenVox is the open source, Puppet-compatible server option discussed alongside this integration. Its install documentation states that OpenVox 8.11 is functionally equivalent to Puppet, with the same command names and configuration paths, so a PuppetDB reachable from an existing Puppet stack is the input you should plan around. If you are migrating from Puppet Enterprise, the documentation asks you to run the uninstaller on each node first and to back up /etc/puppetlabs before installing OpenVox. Settle that migration question before you point the catalog module at PuppetDB, because changing the PuppetDB source later means re-checking every dependency link.
What you need before connecting PuppetDB to Backstage
Connecting PuppetDB to Backstage depends on four things working before any catalog configuration starts: a Backstage backend that runs the catalog, a PuppetDB endpoint that backend can reach, a credential with read access, and the certificates PuppetDB expects. Each card below covers one of these pieces or a related decision, so you can confirm them in order.
A running Backstage catalog backend
The PuppetDB module runs inside the Backstage backend, so you need a working backend with the software catalog enabled before adding it. Confirm that the backend starts, that the catalog accepts a test descriptor, and that you can see its logs. Check the module's installation steps and configuration keys in the plugins directory of the core repository, because the package name and config shape must match the Backstage version you run. Plan for the module to be upgraded alongside the rest of your @backstage packages.
A reachable PuppetDB endpoint
Backstage must reach the PuppetDB HTTPS API from the host or pod that runs the backend, which often sits in a different network zone from the Puppet server. Confirm the host name, port and firewall rules from that exact runtime, not from your workstation. Note which PuppetDB instance is authoritative if you run several, because each one reports its own view of node facts and resources.
A read-only service credential
Create a dedicated certificate or token for the catalog module and grant it read access to node and resource queries only. Do not reuse an administrator certificate or a personal account. Store the credential in your secrets manager and reference it from configuration rather than committing it to the repository. Ask your Puppet team which RBAC or certificate policy governs this principal, because the query scope determines which nodes appear in the catalog.
Client certificates and the CA chain
PuppetDB normally requires mutual TLS, so the backend needs a client certificate, its private key and the CA bundle that signed the PuppetDB server certificate. Confirm that the certificate's subject is authorized in your Puppet setup and that the chain validates from the Backstage runtime. Certificate expiry is a common cause of silent catalog sync failures, so record the expiration date and assign an owner for renewal.
OpenVox as the Puppet source
If your estate runs open source Puppet, OpenVox is the community implementation, and its install guide lists openvox-server and openvoxdb among the packages to install. Puppet and OpenVox cannot coexist on one system, and OpenVox packages replace legacy Puppet packages. Back up the whole /etc/puppetlabs tree before you change anything, and if you run Puppet Enterprise, run the uninstaller on each node first. Plan this as a separate change window, not part of the Backstage work.
Module and API compatibility
Before you configure anything, confirm that the catalog module version in your Backstage repository supports the PuppetDB API version your server exposes. Check this against your own repository and the PuppetDB release you run, not against assumptions from older examples. Record both versions in your change ticket so a later Backstage or PuppetDB upgrade triggers a compatibility check.
Configuring the PuppetDB catalog module step by step
This sequence takes the PuppetDB catalog module from installation to a working import, so that Puppet-managed servers appear in the Backstage catalog as Resource entities that services can depend on. Each step ends with a check you can run before moving on.
Confirm the backend and module source
A backend engineer checks that the Backstage app is on a release line you intend to keep and that the backend package is the one generated from the current template. Open the plugins directory in the core Backstage repository and locate catalog-backend-module-puppetdb, then read its README for the options it expects in your version. Confirm that the PuppetDB instance you plan to query is reachable from the Backstage backend host, since the module runs there, not in the browser. Done when you have the module name, its README open, and a written list of the PuppetDB host and port the backend must reach.
Install the module package in the backend
From the backend package directory of your Backstage app, add the PuppetDB catalog module with the workspace package manager so it is versioned with the other @backstage packages. Keep the module on the same release line as the rest of the app, because mismatched packages are a common source of startup errors after an upgrade. Check that the package appears in the backend dependencies and that the lockfile changed in the same commit. Done when the backend installs cleanly and starts without importing the module yet.
Add PuppetDB connection and certificate settings
In the backend configuration file for the environment you run, add the PuppetDB base URL and the TLS material the backend uses to authenticate to PuppetDB. Store certificate paths and any secrets through your normal secret injection rather than committing them to the repository. Use a dedicated read-only certificate for Backstage where your Puppet CA policy allows it, so access can be reviewed and revoked separately from administrators. Check the configuration by starting the backend in a non-production environment and confirming the log shows the PuppetDB client initialized with no certificate or connection errors.
Define the entity mapping and schedule
Set the module's entity mapping so that each node returned by PuppetDB becomes a Resource entity with a name, owner and type you can filter on, and set the refresh schedule for how often the backend queries PuppetDB. Decide which node facts or certname patterns become the entity name and whether environments map to namespaces or tags, and write the choice in your team's design notes. Keep the schedule modest and compare the load with your PuppetDB sizing before raising it. Done when the configuration file contains the mapping and schedule and the backend logs a completed run on the first start.
Register the provider in the backend
Add the PuppetDB entity provider to the catalog plugin in the backend entry point, so the catalog starts the scheduled import with the rest of the backend. Check that the provider is registered once, because duplicate registration produces repeated runs and duplicate entities. Restart the backend and watch the logs for the first scheduled execution. Done when the backend log shows the provider starting and reports a count of nodes read from PuppetDB.
Verify that a test node appears as a Resource
Pick one test node that Puppet manages and that you can identify by name. Query the catalog API for Resource entities with that name, or search for it in the catalog UI, and open its entity page to confirm the kind is Resource and the metadata matches the mapping you defined. Then check that a sample Component with spec.dependsOn pointing at that Resource shows the relation on both pages. Done when the test node resolves to one Resource, the relation renders, and a second run does not create a duplicate. Repeat with one node from each environment before widening the rollout.
How PuppetDB node facts map to Resource entity fields
This table shows how each part of a PuppetDB node record can map to a Backstage Resource entity, and whether the team must design that mapping. Fields marked as transformer-dependent change with the custom transformer you write, so confirm fact names and class data in your own PuppetDB before you finalize the design.
| Puppet source | Backstage field | Design status | Notes for the team |
|---|---|---|---|
| Certname | metadata.name | Team must design | Choose a naming rule that is unique and valid as a Backstage entity name, and decide whether to keep the full certname or a shortened host name. |
| Environment | metadata.labels or metadata.tags | Team must design | Decide whether environment becomes a label for filtering or a tag. Confirm the environment field name in your PuppetDB before mapping it. |
| Operating system and host facts | metadata.annotations | Depends on transformer | Facts are only available if your PuppetDB stores them and your transformer reads them. Confirm each fact name against your own PuppetDB. |
| Puppet classes or roles | metadata.labels or metadata.tags | Depends on transformer | Class or role data depends on how your control repository assigns them. Map only the classes that help teams find or own a host. |
| Owner for the node | spec.owner | Team must design | Owner usually comes from a fact, a class parameter or an external inventory. Agree the source with the team that owns the Puppet code. |
| Resource type | spec.type | Team must design | Pick a small, documented set of values such as server or virtual machine, and apply them consistently in the transformer. |
| Service relationships | spec.dependencyOf on the Resource | Team must design | Services declare dependsOn on the host Resource. Decide who maintains these links, since PuppetDB does not know which services run on a node. |
| Lifecycle and removed nodes | Entity status or removal rules | Depends on transformer | Decide what happens when a node is removed from PuppetDB. Confirm how your chosen transformer handles stale entities. |
Linking services to hosts with dependsOn and dependencyOf
The import creates one Resource entity per node the PuppetDB catalog module reads, so the entity name is derived from the Puppet certname rather than from a hostname a team typed by hand. Before service owners write any relation, the platform team should confirm what names the import actually produces in the catalog and agree on a single naming rule for the whole estate.
- Certname as the identifier: Confirm which certname format your Puppet estate uses, since a short hostname and a fully qualified name are different strings to the catalog.
- Test import on a small node set: Run the import against a limited set of nodes first and inspect the Resource entities that result before enabling the full estate.
- Owner and type fields: Decide how imported Resources receive
spec.ownerand which type value identifies them, so filtering and ownership views stay consistent. - Stale node handling: Agree on what happens when a node leaves PuppetDB, because a dependency pointing at a removed Resource will otherwise look healthy in the portal.
- Module configuration: Review the catalog-backend-module-puppetdb settings in your Backstage configuration with the version you deploy.
Ask your Puppet and Backstage owners to sign off on the naming rule before any descriptor references a host.
A service owner declares a dependency in the catalog-info.yaml descriptor of the Component. The entry under spec.dependsOn points to a Resource by its entity reference, and that reference must use the same name the import created from the Puppet certname. Once the relation exists, the Resource shows the Component in its dependencyOf list, so the host page answers the question of which services run on it.
- Reference format: Use the entity reference form, such as
resource:default/followed by the certname, and check the spelling against the imported entity. - One relation per host: List each host the service runs on, so an incident on one node shows every affected component.
- Own only your components: Declare dependencies on the Component you maintain, and leave the Resource itself to the platform team.
- Check the reverse view: After merging, open the Resource page and confirm the Component appears under dependency relations.
- Avoid guessing names: If the entity does not resolve, ask the platform team for the exact certname rather than editing the reference until it matches.
Before merging, confirm the reference resolves to a Resource in the catalog and that the dependent Component appears in its relations.
Once the import and descriptors are in place, the relation needs ongoing checks, because Puppet nodes are added, retired and renamed on their own schedule. Broken references usually surface as Components pointing at Resources that no longer exist, and they tend to appear after a node rebuild or a certname change. Treat the dependency graph as operational data that is reviewed, not as a one-time setup.
- Reference validation: Run catalog validation or the catalog processing logs after changes to find dependsOn targets that do not resolve.
- Rebuild events: Review dependencies whenever a node is reprovisioned, since a new certname can leave old relations orphaned.
- Ownership review: Confirm each Component still has an active owner, so dependency questions during an outage reach someone who can act.
- Scheduled reconciliation: Decide how often the imported Resources and the descriptors are compared with the live Puppet inventory.
- Documented procedure: Write down how a service owner requests a new host relation and who approves it.
Before relying on this view during an incident, confirm that broken references are caught by a routine check and not discovered during the outage.
Securing the PuppetDB connection and catalog data
The PuppetDB import is a read path into your configuration management data, and the entities it creates expose how your infrastructure is laid out. The cards below cover the credential, the transport, the secret storage, the scope of what gets imported, and the sign-in model that controls who can see the result.
Least-privilege read access
The backend needs to read node and resource data from PuppetDB, and nothing more. Ask your Puppet team to issue a credential that is scoped to query endpoints and confirm that it cannot submit catalogs, run commands or modify node classification. Review who holds the credential and document the owner. Treat the PuppetDB account as a separate identity from any human administrator account so that rotation and revocation do not affect operators.
TLS verification of the endpoint
Configure the backend to verify the PuppetDB server certificate against your internal certificate authority rather than disabling verification to get the import working. A disabled check lets anyone on the network path impersonate PuppetDB and feed false host data into the catalog. Confirm the hostname in the certificate matches the URL in the configuration, and test a deliberately wrong CA to make sure the connection fails.
Certificate and token storage
Keep the client certificate, its private key and the access token out of the Backstage repository and out of app-config.yaml values committed to Git. Reference them through environment variables or a secrets store your platform already uses, and mount files with restrictive permissions in the container. Record where each secret lives, who can read it, and how it is rotated, because the import stops silently when a credential expires.
Environment and node group scope
Decide which environments and node groups become Resource entities before enabling the module. Production, staging and development hosts often differ in sensitivity, and a broad import puts every host name into the portal. Filter by environment or by classification so that only hosts supporting catalogued services appear. Write the filter down as a reviewed setting, and confirm the filter against the PuppetDB query behavior in your version before relying on it.
Catalog data as infrastructure map
Resource entities, their relations and their annotations can reveal network layout, host roles and which services depend on which machines. Treat the Backstage catalog as a sensitive inventory. Limit who can read Resource entities in the catalog and in search, and avoid copying detailed host facts into entity metadata that does not need them.
Sign-in and guest access
Imported hosts are only as protected as the portal that displays them. Follow the Backstage authentication guidance: do not offer guest access in production, and configure sign-in resolvers so every provider maps to the same identity. Without consistent identity resolution, a user who signs in through two providers can end up with different catalog permissions, which weakens any access rule you place on Resource entities.
Why imported nodes are missing or duplicated
This table pairs each common failure in a PuppetDB to Backstage import with its likely cause and the fix to try first. Use it when nodes are absent from the catalog, appear twice, linger after decommissioning, or leave dependsOn links unresolved. Check the connection and credentials first, then the query filters and name mapping, since most symptoms trace back to one of those.
| Symptom | Likely cause | Fix |
|---|---|---|
| TLS or certificate errors on the PuppetDB connection | The Backstage backend does not trust the CA that signed the PuppetDB certificate, or the client certificate and key it presents are wrong or expired. | Confirm the PuppetDB endpoint and the CA chain used by the Backstage backend, then point the module at the correct certificate files and check the certificate dates. |
| Import runs but returns no nodes, or access is refused | The credentials used by the catalog module lack read access to the PuppetDB endpoints the query needs, or the Puppet access rules were not updated for the new client. | Grant the module's identity read-only access to node and resource queries in Puppet's access control rules, then re-run the import and check the backend logs. |
| Expected nodes are missing from the catalog | A query filter or environment setting excludes them, for example a filter on Puppet environment, a node status condition, or a query that only matches a subset of certnames. | Compare the filter in the module configuration with the nodes you expect, widen it temporarily, and confirm the environments in scope match the ones your nodes report. |
| The same server appears as two entities | The certname PuppetDB reports (often a fully qualified name) differs from the hostname your other sources use, so each source creates its own entity. | Choose one naming convention for Resource names, apply a consistent transform in the import, and remove the duplicate entity created by the other source. |
| Decommissioned servers stay in the catalog | Entities created by the PuppetDB provider are not removed when the node leaves PuppetDB, or the provider is not configured to reconcile deleted nodes. | Review the provider's stale-entity handling and its schedule, and confirm that nodes removed from Puppet disappear on the next run before you rely on the catalog as an inventory. |
| dependsOn references show as unresolved | A Component points to a Resource name that does not match the name the import created, often because of case, domain suffix or a typo in the descriptor. | Compare the reference in the descriptor with the imported entity name, correct the reference or the naming transform, and confirm the entity kind is Resource. |
| Node visible but attributes or owner are wrong | Ownership or metadata was not mapped from Puppet data, so the entity relies on defaults or on a stale value from an earlier run. | Map the Puppet facts or classes you rely on to entity fields explicitly, and check that the mapping is applied on every run rather than only on first import. |
Questions teams ask before importing PuppetDB nodes
How much effort does importing PuppetDB nodes into Backstage take?
The effort depends mostly on how consistent your Puppet estate is, not on the Backstage side alone. The catalog module reads nodes from PuppetDB and turns them into Resource entities, so the work is largely deciding how each node maps to a name, an owner and a service relationship. Teams with a clean certname scheme, consistent role and environment facts, and a short list of services that run on each host usually finish the mapping faster. Ask any partner to size the work from a node inventory sample, the number of environments, and how many services need spec.dependsOn links to hosts, not from node count alone.
What drives the cost of this import up or down?
Four factors move the cost most: the number of nodes that need to be represented, how inconsistent their certnames and facts are, how many Puppet environments and code branches must be reflected, and how much ownership data has to be agreed with service teams. Cost components are the discovery and mapping work, configuration and testing of the catalog module, the decisions about naming and ownership, and the handover. Most of this is one-time. Ongoing costs are the operation of PuppetDB and Backstage and any upkeep of the mapping rules as nodes change. Ask for each component to be priced separately in the written scope.
Can our team import PuppetDB nodes into Backstage without a partner?
Yes, if you have engineers who already operate Backstage and PuppetDB and can work with the catalog descriptor format. The core Backstage repository includes the PuppetDB catalog module, so no third-party plugin is required for the basic import. The harder parts are usually organizational: agreeing on who owns each host, deciding which services depend on which resources, and keeping the mapping current as Puppet changes. A partner is most useful when the team lacks time for that agreement work or has no one who has run the catalog module before. Either way, plan a test environment first.
What do we need to provide before the import starts?
You need read access to the PuppetDB instance the catalog module will query, with credentials scoped to what the import requires. You also need a non-production Backstage instance and a test PuppetDB or a representative subset of nodes, so mapping rules can be checked before they touch the production catalog. A written naming standard is important: it defines how certnames become Resource names and how environments and roles appear in entity metadata. Finally, name the owning team for each service or host group so ownership can be set during the import rather than afterward.
How long does the import take, and what changes the timeline?
No duration can be given from this page alone. The timeline depends on the number of nodes in scope, the number of environments, how well names and ownership are already documented, and how many services need dependency links. Naming and ownership decisions often take longer than the technical configuration, so they are the first thing to schedule. Ask for the schedule to list discovery, mapping, test import, review with service owners, production import and handover as separate milestones, with the dependencies between them stated.
Do we need OpenVox to use the PuppetDB catalog module?
Not necessarily. The catalog module reads from PuppetDB, and PuppetDB is part of the Puppet ecosystem that OpenVox also packages. OpenVox is the open source implementation of the Puppet configuration management platform, maintained by Vox Pupuli under a dual CC BY-SA 4.0 and Apache 2.0 license. If you already run Puppet, you can confirm compatibility of your PuppetDB version with the module before deciding. If you plan to move from Puppet Enterprise or Puppet packages to OpenVox, that is a separate project. Note that Puppet and OpenVox cannot be installed on the same system, and the install guide advises backing up /etc/puppetlabs first. Confirm the target versions with the vendor before scheduling.
How do we scope a fixed-price engagement for this import?
Start with a written scope that lists the PuppetDB instances in scope, the environments, the number of nodes to be represented, the naming and ownership rules, the Backstage environments involved, and the acceptance criteria for the test and production imports. Fixed-scope work depends on those items being agreed before work begins. Ask for the quote to name each deliverable, the assumptions behind it, what the client must provide and by when, and what changes would require a change order. Ask whether ongoing updates to the mapping are included or whether they fall under a separate support arrangement.
How DeployOpen scopes and delivers the PuppetDB import
DeployOpen approaches the PuppetDB import as a bounded integration with a written scope, a tested catalog output and a handover your team can operate. The work runs inside your own infrastructure, so the PuppetDB endpoint, credentials and resulting catalog data stay under your control throughout.
How we scope
- Confirm which PuppetDB instance, environments and node groups feed the catalog, and which are excluded
- Agree how Puppet nodes map to Resource entities, owners, lifecycle states and dependsOn relations
- Record the Backstage and PuppetDB versions, network paths and credential model in the written scope
- Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts
How we build
- Configure the PuppetDB catalog module in your Backstage backend with a read-only query account
- Define the Resource descriptor template and the rules that link hosts to the services running on them
- Test ingestion against a non-production PuppetDB, checking entity counts, names and removed nodes
- Confirm dependency links from services resolve to the right hosts before anything reaches production
How we hand over
- Deliver the configuration, query filters and descriptor conventions as version-controlled files
- Document the refresh schedule, the orphan handling rules and how to add a new node group
- Walk your platform and Puppet teams through the runbook and the test cases used to verify the import
- Provide a list of open items your team owns, such as vendor confirmations on the module's supported options
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after handover
- Retainer work can cover Backstage version bumps that affect the PuppetDB module and its configuration
- Track OpenVox or Puppet platform changes that affect the PuppetDB data your catalog relies on
- Agree support scope, response expectations and change handling in writing before the retainer starts

