Creating GitLab projects from Backstage templates

Backstage templates can create GitLab projects in the right group with the right token scopes, and open merge requests against existing repositories. This page covers the setup, placement rules and failure points to plan for.

On this page

How Backstage creates GitLab projects from a template

The platform team wants new services created in GitLab from a golden path, so that every project starts with the same structure, pipeline stubs and catalog metadata instead of a hand-built repository. Backstage handles this through the GitLab action module in the Software Templates scaffolder. After the template's parameters are collected and the skeleton is rendered, the GitLab action publishes the result to a GitLab group or project, and the same approach applies whether the target is a self-managed GitLab instance or gitlab.com. Confirm the action module's exact name, its published options and the fields it accepts against the current Backstage scaffolder documentation before you write templates, because option names change between releases.

The action's main design decision is placement. A template should name the target group and project path explicitly, and the team should agree on who may create projects in each group, since the scaffolder's identity and token decide what it can create. Token scopes need the same care. Confirm with your GitLab administrators which token type the action expects, which scopes it needs to create projects and push the initial commit, and where that token is stored, so that the scaffolder does not run with broader rights than the template requires. Also confirm how your self-managed instance handles group creation and default branch settings, because a template cannot override a restriction the instance enforces.

Merge requests cover the other common case: a template that changes an existing repository, such as adding a pipeline file or updating a README across many services. In that case the action opens a merge request for review rather than writing directly to the default branch, which keeps the change visible to the owning team. Plan for what happens when the target repository already exists, when a branch name collides, and when the merge request is left open. Test each path with a non-production group before any template reaches developers, and confirm the behavior of each option in your Backstage version, since the scaffolder's handling of existing repositories is the detail most likely to differ from your assumptions.

What you need before the first template run

A GitLab template run depends on five things lining up: a supported Backstage instance, a GitLab target with a group ready to receive projects, a service identity with a narrow token, network paths from the backend, and a plan for the GitLab release line you run. Check each one before writing the first template, because failures here look like template bugs.

Backstage instance and Node.js

Confirm the Backstage app runs on a supported Node.js release. Backstage supports two adjacent even-numbered releases, and as of Backstage 1.46.0 these are Node.js 22 and 24. Check your version against the versioning policy before you add the GitLab action module, since the module versions move with the @backstage packages. Upgrading Backstage later should move all @backstage packages together with yarn backstage-cli versions:bump.

GitLab target and owning group

Decide whether you publish to a self-managed GitLab instance or to gitlab.com, and name the group that will own new projects. The group sets the namespace, inherited permissions and default settings every generated project receives. Ask the platform team which subgroup new projects belong in, and whether that group allows project creation by a service identity. Confirm this in your own GitLab, because group settings vary by organization.

Service identity and scoped token

Create a dedicated service account or bot user for the scaffolder, and issue it a personal or group access token with only the scopes the template needs. Store the token in your secrets system and reference it from the Backstage configuration rather than committing it. Confirm the token scopes and expiry with your GitLab administrator, and plan a rotation date so a silent expiry does not break every template at once.

Network reachability from the backend

The Backstage backend, not the browser, calls GitLab to create projects, push content and open merge requests. Confirm that the backend host can reach the GitLab API and Git endpoints over HTTPS, including any internal certificate authority your GitLab uses. Test from the same network segment and runtime identity the backend uses in production, since a laptop test can pass while the cluster fails.

Offline and air-gapped skeleton sources

Self-managed GitLab can run in offline or air-gapped environments, and that changes how templates fetch skeletons. If the backend cannot reach public Git hosts or package registries, host the skeleton repositories and any template dependencies on GitLab or an internal mirror, and update the fetch URLs in each template. Confirm this before you design the template, because a skeleton that fetches from the internet will fail in a restricted network.

GitLab release line and upgrade stops

Record the GitLab version your instance runs and plan the upgrade stops that apply to it. GitLab requires upgrades to pass through required stops, which since GitLab 17.5 fall at versions x.2, x.5, x.8 and x.11. Action module behavior and API calls can change between releases, so test your templates against the target version before any GitLab upgrade and schedule that test as part of the upgrade plan.

Configuring the GitLab action and the first project

This sequence takes a Backstage instance from no GitLab connection to a template that creates a test project in the right group and registers it in the catalog. Each step closes with a check so problems surface before developers use the template.

  1. Create a scoped GitLab token for the scaffolder

    A GitLab administrator creates a dedicated service account or token scoped to the target group, not a personal access token from an engineer. Grant only the scopes needed to create projects and push code, and confirm the account has the Developer or Maintainer role in that group. Your GitLab administrator should verify the exact scope names against the current GitLab authentication documentation for your version. Done means the token works with a read call against the group's API, and the account can see the group in the GitLab UI.

  2. Add the GitLab integration host and token

    A Backstage administrator adds the GitLab host, including your self-managed domain or gitlab.com, and the token to the integrations section of the app configuration. Store the token in an environment variable or your secrets manager rather than in a committed config file. Restart the backend so the integration loads. Done means the backend starts without integration errors in the logs.

  3. Confirm the scaffolder can reach the host

    From the environment where the Backstage backend runs, check that it can make outbound HTTPS requests to the GitLab host and that the token authenticates. For self-managed instances, confirm any custom certificate authority is trusted by the backend process. Done means a simple authenticated API request from the backend host returns the group details without a TLS or 401 error.

  4. Write a template that calls the GitLab publish action

    A template author adds a template.yaml with a parameter step for the project name and target group, a fetch step for the skeleton, and a publish step that uses the GitLab publish action with the repository URL built from the host and group. Set the project name and group from parameters, and keep the group path fixed where teams should not choose it. Done means the template validates in the template editor and the publish step shows the expected repository URL.

  5. Run a test project creation in a sandbox group

    The author runs the template against a dedicated sandbox group with a throwaway project name. Check the task log for each step, and confirm the new project appears in that group with the skeleton files committed to the default branch. If your team uses dry runs, run one first to catch parameter errors without creating a repository. Done means the project exists in the sandbox group with the expected files and visibility setting.

  6. Register the output in the catalog

    The template's register step adds the new repository's catalog-info.yaml to the catalog as a location. Run the template again in the sandbox and open the links shown on success. Confirm the component appears in the catalog with the correct owner and system. Done means the entity is visible in the catalog with a link back to the GitLab project and no unresolved location errors.

Choosing group and project placement for new repositories

A single shared group per business domain collects every project a domain creates, such as payments, claims or identity. Templates write new projects into that one group, so the group becomes the place where domain-wide policy is applied once. This model suits organizations with a small number of domains and a platform team that owns the templates, because the group's settings, members and approval rules are easy to audit in one place. The trade-off is that individual teams have less room to tune settings, and a large group can make access reviews harder to read.

  • Single point of policy: Default branch protection, approval rules and visibility apply to every new project in the group unless a project overrides them.
  • Parameter-driven target: A template parameter such as a domain picklist sets the target path, so developers cannot place projects in an unapproved group.
  • Access review scope: Membership of one group is the access record for every project in the domain, which simplifies periodic reviews.
  • Limited team autonomy: Teams that need different merge rules must request exceptions or use subgroups.
  • Group size: Very large groups with many projects can be slow to browse, so check how your instance handles search and listing at your expected size.

Confirm the group visibility default and approval settings on your own instance before publishing the template, because the defaults can differ between self-managed installs and gitlab.com.

Changing existing repositories through merge requests

A template should update an existing repository only when the change is standard and the owning team has agreed to receive it, such as adding a .gitlab-ci.yml include, a linting configuration or a dependency policy file. In that case the template should open a merge request rather than push to the default branch. A direct push skips the owner's review, can break a pipeline that the team depends on, and leaves no record of who approved the change. The scaffolder's GitLab action module can create a branch, commit the files and open the merge request in one run, so the template author decides the branch and review path up front rather than leaving it to whoever triggers the run.

Name the branch so it is unique, traceable to the template and easy to filter, for example with the template name and a run identifier in the branch name. Assign reviewers and approvers in the merge request step, using the owning team's group or a designated maintainer rather than the person who ran the template. Confirm in your own GitLab instance how approval rules, code owners and protected branches apply to the merge request, because these settings decide whether the change can be merged without the owner. The token used by the action needs permission to create branches and open merge requests on that project. Check the required token scope against the GitLab authentication documentation for your version, and keep that token limited to the projects the template is meant to touch.

The template must not overwrite files that a team has customized. Before writing, have it check whether the target file exists and whether its content matches the version the template last produced, and skip or flag the file when it does not. Where a file differs, the safer result is a merge request that adds a new file for review, or a note in the description listing what was skipped. Decide with the owning teams which files are managed by the template and which belong to them, and document that split in the template. Test this behavior on a sample project before rollout, since a repository that has drifted from the template is the most common source of unexpected changes.

Token scopes and credential choices for GitLab

The token behind a GitLab action decides what a template can create and how far a leaked credential can reach. Each card below covers one decision the platform team should make before the first template runs against a self-managed or gitlab.com instance.

Personal access tokens are the wrong default

A personal access token acts as one person, so projects appear under that person's identity and the token stops working when they leave or change roles. For Backstage templates that create projects in shared groups, this ties production scaffolding to an individual. Use it only for a short proof of concept, and record that choice so it gets replaced before rollout.

Group and project access tokens

Group and project access tokens belong to GitLab rather than a person and are scoped to one group or project. A group token can create projects only where its group grants the right role, so placement is limited by design. Confirm in the GitLab authentication documentation whether the role and scope you need can be set on a group token in your version, and whether project creation under subgroups is included.

Service account tokens for automation

Service accounts are non-human users created for automation, with their own group memberships and token lifecycle. They suit a platform team that scaffolds across several groups because access can be granted to exactly the namespaces the templates target. Check with your GitLab administrator whether service accounts are available on your tier and whether they can own tokens that do not expire with a person.

Match scopes to each template

List the API operations each template performs: creating a project, pushing an initial commit, opening a merge request against an existing repository, and registering the result. Grant only the scopes those operations need, and create separate credentials for templates that only create projects versus templates that edit existing repositories. Verify exact scope names against the GitLab authentication documentation for your version, since names change between releases.

Merge request changes need write access

Templates that update existing repositories through merge requests need write access to branches and permission to open merge requests, which is broader than creating a new project. Keep that credential separate from creation credentials and limit it to the groups where those changes are expected. Protected branches still apply, so test whether the token can push to the branch your template targets.

Plan rotation and expiry before go-live

Every token needs an expiry date, an owner, and a documented rotation step that updates the Backstage configuration secret without downtime for running templates. Decide who receives expiry alerts and how a rotation is tested in a non-production instance. Store the token in your secrets system rather than in a configuration file, and confirm the expiry and rotation options your GitLab version provides.

Mapping template parameters to GitLab groups, projects and owners

This table shows which Backstage template parameter controls which GitLab setting, and where the matching catalog field records the result. Read each row as a question to answer during template design: where does this value land in GitLab, and where can an operator see it afterward. The main takeaway is that owner mapping fails most often when the Backstage user or group reference does not match a GitLab identity or group path.

Template parameterGitLab setting it controlsCatalog field that records itCommon mismatch to check
Target groupNamespace (group or subgroup) that receives the new project, set by the group path or ID the action resolvesmetadata.annotations entry holding the project URL, and spec.owner on the new ComponentGroup path typed with the wrong case or nesting, or the publishing token lacks rights to create projects in that group
Project nameProject path and name inside the chosen group, which also becomes the repository URL segmentmetadata.name of the Component, which must be a valid catalog name and usually matches the project pathNames that GitLab accepts but the catalog rejects, or a project path that already exists in the group
VisibilityProject visibility level (private, internal or public), subject to the group's own visibility ceilingNot usually stored as a catalog field, so record it in an annotation or a spec.lifecycle note if auditors need itA subgroup cannot grant more visibility than its parent allows, so the request can be silently narrowed or refused
OwnerProject membership and maintainer roles, typically granted to a GitLab group rather than to individualsspec.owner on the Component, set to a Backstage group or user entity referenceA Backstage user with no matching GitLab account, or a Backstage group whose GitLab group was never created, leaves the project with the wrong owner or none
Default branchDefault branch name for the new repository, which the template may set explicitlyUsually not a catalog field, but it appears in the repository location the catalog readsCatalog discovery or links that still point to a default branch such as main after the template chose another name
Repository descriptionProject description shown in the GitLab interfacemetadata.description on the ComponentLong or templated text that the catalog truncates or that drifts from the GitLab copy

Securing the GitLab publish path

A template that creates GitLab projects holds a credential that can write to your source control, so the publish path needs the same care as the production systems it feeds. The cards below cover who can run templates, where the token lives, how much access it carries, what gets logged, and how the GitLab instance and Backstage stay patched.

Limit who can run publishing templates

Any user who can start a template that creates projects can, in effect, create repositories under the group the token reaches. Restrict templates that publish to GitLab to the teams that own those groups, and review which users may register Template entities at all. Check this in your Backstage permission configuration and confirm that guest or unauthenticated access is off in production, as the authentication documentation advises.

Keep the token out of templates

The access token belongs in backend configuration, never in template.yaml, skeleton files or step inputs. Reference it through Backstage secret configuration so it is injected at runtime. Confirm that templates and their parameters cannot echo the value back to the form, and that the token is not written into created repository files or CI variables by default.

Scope the token to the least role

Use a dedicated token or GitLab group access that can create projects only in the target group, at the lowest role that allows creation. Avoid administrator-level or broad personal tokens, since a leaked token then reaches every group. Ask your GitLab administrator to confirm the role and token scopes, and rotate the credential on a schedule you can enforce.

Keep the token out of logs

Scaffolder task logs are visible to users who can view the task list, so an error message that includes request headers or the token would leak the credential to a wider audience. Test a failed run deliberately and search the task output and backend logs for the token value. Restrict who can read task logs and confirm your log pipeline masks secret fields.

Enable audit logging for project creation

Record who ran each template, which group received the new project and which token performed the write. Compare GitLab audit events for project creation with the Backstage task history, so a project created outside the portal stands out. Confirm with your GitLab administrator which audit events your tier provides, since feature availability differs across tiers.

Review hardening and patch both platforms

Work through the GitLab hardening recommendations for the instance, then set a shared upgrade cadence for GitLab and Backstage. GitLab follows required upgrade stops and backports security fixes to recent monthly releases, and Backstage backports high and critical fixes to releases from the last 6 months. Schedule reviews of both release lines so the publish path never runs on an unsupported version.

Common GitLab publish failures and their fixes

This table pairs each failure a GitLab publish step commonly hits with its most likely cause and the first thing to check. Read it from the error message backward: find the symptom your scaffolder task log shows, then work through the cause column before changing any template or configuration. Most failures trace to the token, the group path, or the network path between the Backstage backend and the GitLab host.

FailureLikely causeFirst thing to check
Token scope rejectedThe token used by the GitLab action lacks the API scope or the write permissions needed to create projects or push branches.Confirm the token's scopes and its user role on the target group, and whether it has expired.
Group not found or no permission to create in itThe group path in the template is misspelled, uses the display name instead of the path, or the token's user is not a member with rights to create projects there.Compare the group path in the template with the full path shown in GitLab, then check the token user's membership and role in that group.
Project name already takenA project with the same path already exists in the target namespace, possibly from an earlier failed run that created it before a later step failed.Search the target group for the project path, and confirm whether an earlier task created it before deciding whether to reuse, rename or delete it.
Host not reachable from the backendThe Backstage backend cannot resolve or route to the GitLab host from where it runs, often because of DNS, egress rules, a proxy or a private network boundary.Run a connectivity test from the Backstage backend host or container to the GitLab host address, not from your own workstation.
TLS certificate not trusted by the Backstage serverThe GitLab instance uses a certificate issued by an internal or private certificate authority that the Backstage Node.js process does not trust.Check the certificate chain presented by the GitLab host and confirm that the internal CA is installed in the trust store used by the Backstage backend.
Merge request creation blocked by branch rulesThe target branch is protected and the token's user cannot push or open merge requests against it, or the template pushes directly to a branch that rules forbid.Review the protected branch settings and the token user's push and merge permissions, then confirm the template opens a merge request rather than pushing directly.

Questions teams ask before building GitLab templates

Does the first GitLab template take much more effort than later ones?

Yes, the first template carries most of the setup work. The first build covers the Backstage side (the scaffolder backend, the GitLab action module, the integration configuration and a sample skeleton) and the GitLab side (the group structure, token and permission model, and the project settings the template applies). Later templates reuse that foundation and mostly add parameters, skeleton content and steps. Effort for later templates depends on how many variants you need, how different their CI/CD and repository settings are, and whether they need custom actions. Ask any partner to split the quote into foundation work and per-template work so you can see what each additional template adds.

What drives the cost of building custom actions for GitLab?

Custom actions cost more when the built-in GitLab action module cannot do a step your process needs, such as applying group-level settings, registering protected branches in a specific pattern, or calling an internal approval system. The main drivers are:

  • The number of distinct steps the action must perform and how many external systems it calls.
  • Input validation with schemas, error handling and idempotent behavior so a failed run can be retried safely.
  • Dry-run support and unit tests, which need to be designed in rather than added afterward.
  • Documentation for the teams who will call the action from their own templates.

Ask for the action list, the systems each action touches and the test expectations in the written scope.

Can a platform team build GitLab templates without outside help?

A platform team with TypeScript experience, working knowledge of Backstage and administrator access to GitLab can usually build a first template in-house. The harder parts are the ones that require repeated decisions: group placement rules, token scopes that match least privilege, handling of existing repositories through merge requests, and tests that run without creating real projects. Teams that already run Backstage in production often keep template authoring in-house and bring in a partner for the review of token design, the custom actions or a security check before rollout. Decide early which of these you will own.

How long does it take to reach a first working GitLab template?

The duration depends on scope, and no fixed figure applies to every organization. The main factors are how many groups and project types the template must serve, whether the GitLab instance and its tokens are already prepared, whether a Backstage instance is in place, and whether custom actions are needed or the built-in action module covers the first use case. A narrow first template that creates one project type in one group can move quickly once access is granted. Ask for a timeline in the written scope that lists the dependencies, especially GitLab administrator and identity access, which often set the pace.

What does the work need from GitLab and Backstage administrators?

Both sides need to commit people and access before build work starts. On the GitLab side, administrators or group owners need to:

  • Confirm the group hierarchy where new projects will be placed and who may create projects there.
  • Approve the token type and scopes, typically a dedicated service account or group access token limited to what the template needs.
  • Decide how merge requests to existing repositories will be reviewed and which branch rules apply.

On the Backstage side, administrators need to provide a test environment, configuration access for integrations and sign-in, and a review path for new templates. Confirm the exact token and permission options against the GitLab documentation for your version.

Do gitlab.com and self-managed GitLab need different templates?

The template logic is largely the same, but the connection, hosts and some administrative options differ. On gitlab.com, you use the hosted API endpoint and tokens issued by that service, and you do not manage the server, upgrades or backups. On a self-managed instance, you configure the Backstage integration to point at your own host, manage TLS trust, and handle token policies and any instance-level settings that your administrators control. Self-managed deployments in offline or air-gapped networks also need the Backstage container and dependencies available from inside that network. Confirm the instance type, hostnames and network path before writing the integration configuration, because these determine what the template can reach.

Does the GitLab license affect how templates are built?

Your template work targets the GitLab API and features your tier provides, so the license tier you hold determines which settings you can apply from a template. GitLab's source is open core: content under the ee/ directory is under the GitLab Enterprise Edition license, and the rest is under the MIT Expat license. GitLab Free, Premium and Ultimate are all available for self-managed installations, and GitLab's pricing page lists different features for each tier. If a template depends on a Premium or Ultimate feature, such as certain approval or compliance settings, confirm your subscription covers it before design starts.

Who pays for what when building GitLab templates?

Costs fall into a few groups. The one-time items are discovery, template and action design, the build, testing and the first rollout. Recurring items are your GitLab subscription, your Backstage hosting and staff time for template maintenance, GitLab upgrades and Backstage upgrades. If you engage a partner, the quote should separate fixed-scope build work from any retainer for later support. DeployOpen does not publish a price list, and a quote follows from the written scope. When you request one, ask for the deliverables, the assumptions about GitLab access and the test expectations so you can compare proposals on the same basis.

How DeployOpen scopes and delivers this integration

DeployOpen plans, implements, tests and supports self-hosted open-source platforms inside your own infrastructure, and that includes the GitLab side of a Backstage template: the scaffolder action module, the group and project placement rules, the merge request flow for existing repositories and the token scopes the portal uses. DeployOpen is a Chainscore Labs company. The work runs in your cloud accounts, data centers or air-gapped network, so the GitLab instance, the Backstage backend and the credentials stay under your control. DeployOpen is not an official GitLab or Backstage partner, reseller or certified provider, and this page makes no claim to that status.

Most engagements are fixed-scope. Before work starts, the written scope names the GitLab deployment you are targeting (self-managed or gitlab.com), the groups where new projects will land, the templates in scope, the custom actions to build or review, the test and dry-run approach, and the handover documents. Deliverables and timelines are agreed in writing, and a quote follows from that scope. Ask any provider to show which items are in the quote and which are assumptions, especially the number of templates, whether existing repositories need merge request automation and whether your GitLab edition supports the controls you need. Ask for the scope of the sign-off test as well, because that is where unclear acceptance criteria usually cause rework.

Ongoing support is available as a retainer, which fits the recurring work this integration creates: keeping @gitlab and @backstage action modules compatible with each Backstage upgrade, rotating project access tokens, reviewing new templates before they reach every developer, and checking GitLab upgrade stops on the instance that receives the projects. Ask whether the retainer covers Backstage version bumps and GitLab patch releases, how urgent security fixes are handled and who holds the credentials used for support. Confirm those terms in the written agreement rather than assuming them from a general support description.

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.