How the Backstage GitLab integration finds catalog entities
Your platform team keeps service descriptors in GitLab projects, and the portal should show them without someone copying files by hand. Backstage connects to GitLab through its GitLab integration and discovery configuration, and the exact package names and configuration keys should be confirmed against the Backstage integrations documentation for the release you run. The connection does three jobs: it discovers catalog files in GitLab projects, it can import GitLab users and groups as organization data, and it can refresh entities when a push event arrives instead of waiting for a scheduled scan. The code is open source under the Apache License 2.0, as shown in the backstage/backstage repository on GitHub, and the project offers no paid Backstage edition, so the cost sits in the people who configure, host and maintain the portal.
Discovery is where most early decisions sit. You choose the GitLab host (gitlab.com or your self-managed instance), the credential Backstage uses to read projects, and the scope it may search, such as specific groups, branches and whether forks are included. Each choice changes what appears in the catalog. Scanning every project in a large GitLab instance can create noise, duplicate entities from forks, and entities drawn from feature branches that no one intends to publish. Ask the team to agree on one default branch rule and one file path convention before enabling discovery, and confirm how the GitLab catalog module applies filters, because the options differ by version. Test with a small group first and check the entities that appear against a list you wrote in advance.
Org data and webhooks need their own plan. Importing users and groups from GitLab can make ownership fields in the catalog match your source control roles, but it must not be confused with sign-in, which is configured separately. Decide whether GitLab is the source of truth for team membership or whether your identity provider is. Webhook refresh only works if your Backstage backend is reachable from GitLab over the network path you actually use, and if the webhook secret and event types are set on both sides. Confirm which push events trigger a refresh in your Backstage version, and keep a scheduled sync as a fallback so a missed event does not leave stale ownership in the catalog.
What you need before connecting Backstage to GitLab
The GitLab integration depends on a few components that must be in place before any catalog discovery runs. Confirm each one on the Backstage side and on the GitLab side, because most first-time failures come from a gap between the two.
Backstage on a supported release line
Run your Backstage instance on the main release line or the next line, and confirm the Node.js version against the versioning policy before you install the GitLab modules. Backstage supports two adjacent even-numbered Node.js releases, and as of Backstage 1.46.0 these are Node.js 22 and 24. Check the policy for the current list, because the supported pair changes over time. Plan the GitLab modules to match the release line you already run so that the @backstage packages stay on one version.
GitLab service identity with group access
Create a dedicated service account or bot identity in GitLab rather than reusing a personal account. Grant it read access only to the groups and projects that hold catalog files. Discovery reads only what this identity can see, so a broad group grant widens the catalog and a narrow one leaves entities missing. Ask your GitLab administrator which groups are in scope and whether subgroups inherit access, since that decides how much the identity can reach.
Access token with documented scopes
Issue a token for the identity with the scopes the Backstage GitLab integration documents, and confirm the exact scope names against the current docs before you create it. Store the token in your secrets system and reference it from configuration rather than writing it into a committed file. Record the token's owner, expiry date and rotation owner, because a token that expires silently stops discovery without an obvious error in the portal.
Network path and certificate trust
Confirm that the Backstage backend, not only your browser, can reach the GitLab host over HTTPS. For self-managed GitLab, the backend must also trust the certificate chain, which usually means adding your internal certificate authority to the container or Node.js trust store. Test from the same host and network that runs Backstage, because firewall rules and proxies often differ from a workstation. A failed TLS handshake looks like a discovery failure, so check trust early.
PostgreSQL for the backend
Provide a PostgreSQL database for the Backstage backend, since production deployments use PostgreSQL and each backend plugin receives its own logical database through Knex. The catalog stores processed entities there, so the GitLab-discovered entities survive restarts and are visible to every replica. SQLite suits local development only. Confirm the database version, backup schedule and credentials with your platform team before the first discovery run.
Catalog descriptor layout in GitLab
Decide where catalog-info.yaml files live before you configure discovery. Agree on whether each repository holds one descriptor at its root or several in subdirectories, and whether forks and archived projects should be excluded. Without this decision, discovery can pick up test copies or duplicate ownership. Write the naming rule down so that teams creating new services know which path the discovery pattern expects.
Self-managed GitLab or gitlab.com: what changes in the setup
A self-managed GitLab instance changes the integration configuration at the host level. Backstage needs the exact base URL of your instance, and every URL it calls, including API and webhook endpoints, must resolve to that host from the machine running the Backstage backend. If your GitLab sits behind an internal certificate authority, the Backstage backend must trust that CA, or TLS handshakes fail before any catalog file is read. Network paths also matter: the backend must reach GitLab over the network you actually route, which may be a private VPC, a VPN or a segment that is not reachable from your developers' laptops.
- Host URL: Set the base URL to the instance hostname and port your users browse to, and confirm whether a sub-path is in use, since the integration matches on the configured host.
- Internal certificate authority: Add the internal root or intermediate CA to the backend trust store, and confirm the same chain is used for any webhook receiver that Backstage exposes.
- Network paths: Confirm that the Backstage backend can open outbound connections to the GitLab host on the required ports, and check proxy and firewall rules for both discovery and webhook traffic.
- Air-gapped networks: If GitLab sits in an isolated network, Backstage must run in the same enclave or be granted a controlled path to it. Plan for content from external package registries to reach the Backstage build separately, because the GitLab offline environments guidance does not cover Backstage itself.
- Version alignment: Confirm that your GitLab release is one the integration documentation supports, and plan upgrades around the required upgrade stops.
Before go-live, test discovery from the backend host itself, not from a workstation, because that is the path the catalog processor will use.
Using gitlab.com changes the setup in the other direction: the host is fixed and public, so the work moves to credentials, rate limits and egress. The integration points at the public hostname, and Backstage must be able to reach it over the internet or through an approved proxy. The main decisions are which credential type to use, how discovery is scoped to groups and projects so the request volume stays predictable, and whether your data residency or egress policy allows catalog reads to leave your network.
- Public host: Configure the integration against the hosted gitlab.com address, and confirm that no self-managed CA or custom host entry is required.
- Token scope and lifetime: Choose the token type and scope the credential to the minimum read access needed for catalog files, then confirm the expiry and rotation process in the current GitLab authentication documentation.
- Rate limits: Confirm the current API rate limits for your plan and credential type in the GitLab documentation, and size discovery schedules so full scans do not compete with other automation.
- Data residency and egress: Confirm with your compliance owner where catalog metadata is stored and whether reading repositories from gitlab.com through your egress path is allowed for the repositories in scope.
- Plan considerations: Confirm which tier your organization uses, since features referenced elsewhere in this topic may differ between plans.
Before go-live, check the rate limit and token expiry values in the current documentation for your account, because those values can change and should not be assumed from older guides.
Configuring the GitLab integration step by step
This sequence takes you from a GitLab credential to a first catalog ingestion in Backstage, with a check at each step so you can confirm the connection works before adding the next piece. Confirm every configuration key name against the current Backstage integrations documentation before you apply it, because key names change between releases.
Create a read-only GitLab credential
Your GitLab administrator creates a credential that Backstage will use to read catalog files and group membership. Use a dedicated service account or a group access token with the narrowest read scopes that cover the groups you plan to discover, rather than a personal token tied to one engineer. Store the token in your secrets manager and expose it to Backstage as an environment variable, not in the committed app-config file. Done means the token authenticates against the GitLab API for the target host and can list the projects in your first target group, which you can confirm with a single API call from the Backstage host.
Add the GitLab host to the integrations configuration
In the Backstage app configuration, add an entry under the GitLab integrations section that names your host, such as gitlab.com or your self-managed hostname, and references the token environment variable. For a self-managed instance, also set the base URL and confirm that the Backstage backend can reach it over the network path your organization uses. Confirm the exact key names against the current Backstage integrations documentation for your release. Done means the backend starts without integration errors and a read of a known repository file through the configured integration succeeds.
Configure the GitLab catalog provider
Add a GitLab discovery provider to the catalog configuration. Name the target groups to scan, set the file pattern that matches your catalog descriptor files, and decide whether forks, archived projects and non-default branches are included. Keep the target list short at first so the first run is easy to verify. Confirm the provider key names and filter options against the current catalog documentation. Done means the backend log shows the provider starting and reporting how many projects it examined for the first target group.
Configure org data ingestion for users and groups
If GitLab group membership should populate Backstage users and groups, configure the GitLab org data provider with the same host and credential, and set the group scope it reads. Skip this step if ownership is managed by another identity source, and document that choice so catalog owners are not duplicated. Ingested users must match the identities your sign-in resolvers map to, so review which attribute becomes the entity name. Done means the catalog shows Group entities for the targeted GitLab groups and User entities for their members after the next scheduled run.
Register the push webhook in GitLab
In each target project or group, a GitLab maintainer adds a webhook that points to the Backstage events endpoint for the GitLab integration, with a shared secret that matches your Backstage configuration. Enable push events so a commit to a catalog file triggers an update without waiting for the next scheduled run. Confirm the endpoint path and secret setting against the current Backstage documentation. Done means GitLab's webhook history shows a recent test delivery with a successful response code, and the backend log records the event being received.
Run a first ingestion and verify the entities
Commit a catalog descriptor to one test project in a target group, then trigger a push or wait for the first scheduled read. Open the Backstage catalog and search for the component name from the descriptor. Check that its owner, system and lifecycle fields resolve to existing groups. Done means the entity appears with the expected source location pointing to the GitLab project and branch, and a second commit that changes its description updates the catalog without a restart. Record the result so later changes can be compared against it.
Filtering groups, branches and forks, and reacting to push webhooks
Limit discovery to the groups and projects that should appear in the portal before the first full scan runs. Every project that matches the GitLab discovery settings becomes a candidate for a catalog entity, so a broad match on a large self-managed instance can flood the catalog with repositories nobody owns. Decide which top-level groups, subgroups and project paths are in scope, then write the filter in the GitLab discovery configuration so that only those paths are read. Confirm with your Backstage release which filter options the GitLab catalog module supports for group and project matching, because the set of options has changed across releases and the documentation for your version is the authority. Run a dry scan against a single group first and count the entities it produces against the number of repositories you expected.
Branch selection decides which version of `catalog-info.yaml` the catalog reads. If the configured branch is main but a team keeps its descriptor current on a release branch, the catalog will show stale ownership, lifecycle or dependency data without any error. Forks and archived projects create a different problem: a fork carries a copy of the same catalog-info.yaml, so the same component can appear twice under two owners, and an archived repository keeps a valid descriptor that no longer reflects a running service. Decide explicitly whether forks and archived projects are excluded, and confirm how your release handles them, since the options for excluding forks and archived projects vary by version. Check the result by searching the catalog for duplicate metadata.name values after the first scan.
Push webhooks let Backstage refresh changed entities without waiting for the next full scan. A GitLab project webhook sends push events to a Backstage endpoint, and the catalog re-reads the affected descriptor. Two things must be in place before you rely on this. First, a shared secret must be configured on both the GitLab webhook and the Backstage side, and it must be stored in your secrets manager rather than in the repository. Second, the Backstage backend must be reachable from your GitLab instance over the network path you have approved, which matters for self-managed GitLab in a restricted network. Confirm the webhook event types that your Backstage release accepts, for example whether push events alone are enough or whether tag and merge events also need to be subscribed, and test one change end to end before enabling it across all groups.
How GitLab groups, projects and users map to catalog entities
This table shows which GitLab object becomes which Backstage entity, who owns the data in each case, and where the mapping tends to break. Read it as a checklist for designing the descriptor layout and the identity model before you configure discovery and ingestion. The main takeaway is that catalog ownership only matches reality when GitLab group membership and the sign-in identity resolve to the same catalog user.
| GitLab object | Backstage entity or field | Source of truth | Notes |
|---|---|---|---|
| Project with a catalog-info file | Component, or another kind set in the descriptor | The catalog-info file in the repository, read through GitLab discovery | Kind and lifecycle come from the descriptor, not from GitLab. Confirm the discovery config key and file path against the Backstage GitLab integration docs. |
| Project path under a group | Group path informs the owner reference and naming convention | GitLab namespace path | Path alone does not set ownership. Teams should set spec.owner explicitly so the path is not the only signal. Confirm the exact field names in the descriptor schema. |
| Subgroup or nested group | Group or owner reference used in the descriptor | GitLab group hierarchy | Decide whether nested groups map to one owner or to a parent team. Flattening nested groups can misstate ownership for large organizations. |
| GitLab user | User entity | GitLab directory, read by the GitLab org provider or by an external directory your organization designates | Confirm the org provider module and its schedule in the Backstage docs. Users who are not members of an ingested group may not appear as expected. |
| GitLab group | Group entity with members | GitLab group membership | Membership changes reach the catalog on the provider schedule, not instantly. Note which group is the authoritative team for each owner reference. |
| Sign-in identity (GitLab OAuth or other provider) | Catalog user reference used at sign-in | Identity provider account, resolved through a sign-in resolver | Per the Backstage authentication documentation, configure resolvers so every provider resolves to the same identity. Confirm resolver options before production. |
| Guest or unmatched sign-in | No catalog user, or a placeholder | Sign-in configuration | The authentication guidance advises against guest access in production. Unmatched accounts can show as owners without a matching User entity. |
Securing the GitLab token and the catalog it feeds
The GitLab token and webhook secret give Backstage read access to your source code and push events, so they carry the same weight as production credentials. The cards below cover how to scope the identity, store and rotate its secrets, limit which groups reach the catalog, and keep sign-in aligned with GitLab.
Use a dedicated service identity
Create a separate GitLab service account for Backstage rather than reusing a personal token from an engineer. Grant only the read scopes that catalog discovery needs, and confirm the exact scope names against the current GitLab authentication documentation before issuing the token. A dedicated identity keeps the audit trail readable, survives staff changes, and lets you revoke Backstage access without disrupting people. Check that the account is a member only of the groups it must read.
Keep the token out of plain configuration
Do not place the GitLab token directly in app-config.yaml or in a file committed to a repository. Reference it through an environment variable that your secret store injects at runtime, and confirm that container logs and config dumps do not print it. Review who can read the Kubernetes secret or vault path that holds the value, since that list is effectively the list of people who can act as the Backstage identity.
Rotate the token on a schedule
Set a rotation interval for the token and record the owner, the creation date and the next rotation in your operations runbook. Rotation should overlap: issue the new token, deploy it, verify catalog refresh, then revoke the old one. Ask your team to confirm how the GitLab instance enforces token expiry for service accounts in your edition, because the available controls differ between self-managed and gitlab.com setups.
Protect the webhook shared secret
Push webhooks let Backstage react to changes without polling, but a webhook that accepts unsigned or guessable requests can trigger refreshes from anyone who finds the endpoint. Generate a long random secret, store it with the token, and make Backstage reject events that fail validation. Restrict the endpoint to the network paths GitLab actually uses where your architecture allows it.
Limit which groups the catalog can see
Discovery that walks every group the token can read will copy private repository metadata into the catalog, and the catalog is usually visible to a much wider audience than the source repositories. Define an explicit group and filter list, exclude archived or sensitive projects, and check which catalog pages are visible to each sign-in group. Treat the catalog as a publication decision, not a side effect of reading.
Align sign-in with GitLab
Configure the Backstage GitLab sign-in provider so users authenticate with their GitLab identities, and set sign-in resolvers so every provider maps to the same catalog user. Do not enable guest access in production, because an unauthenticated visitor could otherwise browse the catalog built from private projects. Confirm with your team which GitLab groups grant portal access and test the resolver with a user outside those groups.
Why GitLab entities fail to appear in the catalog
This table maps common symptoms of a broken GitLab connection to their likely causes and to checks you can run yourself. Read it from the symptom column, then work through the cause and fix in order, since a single misconfigured filter or token can produce several symptoms at once. Most failures trace back to scope, TLS trust, webhook reachability or identity matching.
| Symptom | Likely cause | Fix to check |
|---|---|---|
| Entities missing after a discovery scan | The group filter or catalog file path in the GitLab discovery config does not match where the catalog-info file actually lives | Confirm the configured group path and file path against the project in GitLab, then run a single discovery pass and read the log for the project names it visited |
| Private projects skipped or returning authorization errors | The access token lacks read scope or the account behind it cannot see the private projects | Check the token scopes and confirm the token owner is a member of each group that holds catalog files, then test a read of one private catalog file with the same token |
| TLS or certificate errors against a self-managed host | The internal certificate authority is not trusted by the Backstage backend process | Fetch the GitLab host with the same CA bundle the backend uses, confirm the full chain is served, and check that the backend container or host trusts the internal root |
| Webhook deliveries never arrive | The Backstage endpoint is not reachable from GitLab, often because of a private network, proxy or firewall rule | In GitLab, open the webhook's recent deliveries to see the response code or error, then send a test request from a host on the GitLab network to the same URL |
| Webhook received but entities not updated | The webhook secret token in GitLab does not match the value configured in Backstage | Compare the secret token on both sides character for character, rotate it if unsure, and resend a test event to confirm the backend accepts the signature |
| Duplicate entities for the same repository | Forks of a catalog-bearing project are discovered as separate repositories with identical descriptors | Confirm the discovery filter excludes forks, then check whether each fork carries its own catalog file and remove or disable the copies |
| Owners or users do not match their sign-in identity | The GitLab username or email in the catalog differs from the identity the user signs in with, so the sign-in resolver cannot link them | Compare the user's GitLab username and primary email with the sign-in identity, and confirm the resolver in the auth configuration maps to the same field |
Questions teams ask before connecting Backstage to GitLab
How much effort does a Backstage and GitLab integration take?
The effort depends on how many groups and projects Backstage must read, whether the GitLab instance is self-managed or gitlab.com, whether org data from GitLab is in scope, and how many existing repositories need catalog-info files added or corrected. Discovery is a configuration task, but each group you add to the filter, each branch pattern and each fork rule needs a decision and a test. Ask a partner to break the quote into discovery setup, catalog file cleanup, webhook wiring and handover, so you can see which parts scale with your repository count.
Can our in-house team connect Backstage to GitLab without a partner?
Yes, a team with Backstage and GitLab experience can complete the core integration alone, and the Backstage integration documentation covers the configuration keys. The harder part is usually the surrounding work: agreeing on a catalog-info file standard, deciding which projects count as catalog entities, handling forks and archived repositories, and keeping the setup working after upgrades. Estimate the hours your engineers will spend on those decisions, not only on the first working import, before choosing between your own team and a partner.
What do we need from GitLab administrators and identity owners?
GitLab administrators need to create a service credential that Backstage can use to read repositories and, if you ingest org data, read users and groups. They also need to confirm which groups are visible, whether a webhook endpoint can be reached from GitLab, and how the instance's authentication is configured. Identity owners decide whether GitLab users and groups should become Backstage users and groups, and they must agree on the sign-in resolver so one person maps to one identity. Confirm these owners and their approval steps in the first week.
What recurs after the GitLab integration goes live?
Credential rotation is the most common recurring task, because the token Backstage uses must be renewed on your schedule and stored in your secrets system. Backstage upgrades move the integration modules along with the rest of the @backstage packages, and the Backstage upgrade guide describes the bump command and the template changes to review. On the GitLab side, the maintenance policy sets a monthly minor release and required upgrade stops, so any version change may require checking the integration. Filter changes, new groups and webhook reviews also recur as teams reorganize.
Does the GitLab integration need GitLab Premium or Ultimate?
The Backstage integration reads repositories through the GitLab API, so the core discovery path does not depend on a particular GitLab tier. The tier matters for what else you connect: the GitLab pricing page lists different features for Free, Premium and Ultimate, and all three are available for self-managed installations. Confirm with your GitLab account owner which tier you run and whether the API limits, group features or org data you need are available on it. Do not assume a feature exists in your tier without checking.
How do we check that catalog discovery and webhooks are working?
Start with a small, named set of groups and confirm that each expected repository produces one catalog entity with the right owner and type. Then push a change to a catalog-info file and verify that Backstage updates the entity without a manual refresh. Test forks, archived projects and branches outside your default pattern, since these are where filters most often surprise teams. Ask your partner for the test cases they will run and the evidence they will hand over at acceptance.
What should we ask a partner to include in a written quote?
Ask for a scope that names the GitLab edition and deployment model, the exact groups and projects in discovery, whether org data and webhooks are included, and the number of catalog files expected to need cleanup. Request a list of deliverables, such as configuration committed to version control, a runbook for credential rotation and filter changes, and a handover session with your team. Ask which responsibilities sit with you, including GitLab administration and identity decisions, and what happens if the scope changes during the work.
How DeployOpen scopes and delivers the GitLab integration
DeployOpen plans, builds, documents and supports the Backstage and GitLab integration inside your own infrastructure, covering self-managed GitLab or gitlab.com, catalog discovery, org data and push webhooks. Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
How we scope
- Confirm whether your GitLab is self-managed or gitlab.com, and which groups, projects and branches hold catalog files.
- Define the discovery filters for groups, branches and forks so only intended catalog files are read.
- Agree which GitLab users and groups are ingested, and how they map to Backstage ownership and sign-in identities.
How we build
- Configure the GitLab integration with the access credentials your security team approves for read-only discovery.
- Set up the catalog discovery and org data providers, then test them against representative projects and forks.
- Wire push webhooks to catalog refresh and verify that changes appear in Backstage without relying on polling alone.
How we hand over
- Deliver the configuration, credential inventory and filter rules as documented, version-controlled artifacts.
- Provide test evidence showing discovery, org data import and webhook-driven updates against agreed sample projects.
- Walk your platform and GitLab administrators through the runbook for rotating tokens and troubleshooting ingestion failures.
How we support
- Support the integration through a retainer that covers Backstage and GitLab version changes after handover.
- Review integration behavior when Backstage or GitLab releases change the catalog or authentication configuration.
- Re-test discovery filters and webhook delivery as your group structure and catalog conventions evolve.

