GitLab CI/CD secrets from HashiCorp Vault

How to pull CI/CD secrets from HashiCorp Vault into self-managed GitLab jobs using ID tokens, with the Premium tier requirement, Vault roles limited by project and branch, and the checks that close each step.

On this page

GitLab CI/CD secrets from HashiCorp Vault: what the integration does

Many self-managed GitLab instances still hold deployment credentials as long-lived CI/CD variables, where any job that can reference the variable can read the value, and where rotation depends on someone remembering to change it. The native GitLab CI/CD integration with Vault replaces that pattern. A job asks Vault for the secret it needs at run time, using an ID token that GitLab issues for that specific job, and Vault returns a credential that expires. Your secrets manager, not the variable list, becomes the place where access is granted and revoked. The integration is part of the Premium tier, so confirm the tier you are licensing before you plan the design around it. The supported secrets manager is HashiCorp Vault, and GitLab's documentation for the version you run should be checked before you treat a compatible fork such as OpenBao as supported in the same way.

The control point is the Vault role and its bound claims. When a job presents its ID token, Vault checks claims such as the project path, the project ID, the ref and the ref type, then decides whether that job may authenticate and which policy applies to it. A role bound to one project and to a protected branch can return a production database credential, while a role for feature branches receives nothing sensitive. Because the token identifies a project and a ref, a branch name alone is a weak boundary, and a role that matches on the project path can break after a rename, so the bound claims deserve the same review as the policy attached to them. Ask your team to list which roles exist, which projects they cover and who can change them, before any job is migrated.

Before you migrate, confirm three things with your GitLab and Vault documentation for your exact versions: how the ID token audience is set in the CI/CD job configuration, which Vault auth method and mount path your roles use, and whether your Vault edition and storage backend fit the availability and audit requirements you already hold for other secrets. Plan the migration as an inventory exercise. Each existing CI/CD variable should map to a role, a policy and a named owner, and variables that cannot be mapped should be retired or kept only for a documented reason.

What you need before connecting GitLab to Vault

Connecting GitLab CI/CD to Vault touches four systems: the GitLab instance that issues ID tokens, the Vault cluster that validates them, the runners that fetch secrets, and the people who own the policies. Settle each of the cards below before writing any pipeline configuration, because a gap in one usually surfaces as a failed job.

Self-managed GitLab on a supported version

Confirm that your instance runs a release still receiving fixes, and check the current release list rather than assuming a version. Under the maintenance policy, bug fixes go only to the current stable release, while security fixes reach the previous two monthly releases too. Plan the upgrade path through required upgrade stops before you rely on newer ID token behavior. Confirm that the Premium tier is applied to the instance, because the Vault secrets integration requires it.

Reachable Vault endpoint with trusted TLS

Vault needs a stable HTTPS address that every GitLab runner can reach over your network path, including any proxies or egress rules. The certificate chain must be trusted by the runner host, which means an internal certificate authority must be installed where runners execute jobs. Test the chain from a runner, not from your laptop, since runner images and hosts often hold different trust stores.

JWT and OIDC auth method in Vault

GitLab issues a short-lived ID token to each job, and Vault must be configured to validate it. Enable the JWT auth method and point it at the GitLab instance URL so Vault can fetch signing keys. Decide early whether one JWT mount serves all projects or whether you split mounts by group, since that choice shapes how roles are named and audited.

Vault edition and license status

Choose between Vault Community Edition, Vault Enterprise and HCP Vault Dedicated, and record the license terms that apply. HashiCorp moved future releases to the Business Source License 1.1, so confirm with legal how that license affects your use, especially if you offer hosted services. Enterprise features such as performance replication, namespaces and FIPS compliance need a license applied to the cluster, so ask HashiCorp for current Enterprise pricing through its sales team.

Vault version checked against releases

Vault 2.2.0 is the current HashiCorp release at the time of writing, following 2.1.2. Check the GitHub releases page on the day you plan the build, and confirm the exact version your Vault team has approved. If you choose OpenBao instead, verify its migration notes, since its guide was tested against Vault 1.14.1 Community Edition only.

Named owner for Vault policies

Assign one accountable person or team to write, review and retire the Vault policies and roles that bind secrets to GitLab projects and branches. Without a named owner, roles drift as projects are renamed or branches are added, and secrets end up readable by pipelines that should not see them. Agree how changes are requested, reviewed and logged before the first production pipeline reads a secret.

Configuration sequence from Vault auth to a passing pipeline

This sequence connects a GitLab project to a Vault role that issues read-only access to one secret, scoped to a single project and protected branch. It ends with a passing pipeline and audit evidence that the restrictions work, using a non-production project for the test.

  1. Confirm tier and instance network path

    The platform owner confirms that the GitLab edition and tier on your instance include the CI/CD secrets integration, using the current pricing and feature pages. They then record the external URL that Vault will use to reach GitLab's OpenID configuration. Vault must reach that URL over HTTPS from its own network, so the security team checks firewall rules and certificate trust between the two systems first. This step is done when a request from the Vault host to the instance's OpenID discovery document returns a valid response that lists the expected issuer.

  2. Enable and configure the JWT auth method

    A Vault administrator enables the JWT auth method and sets the GitLab instance URL as the OIDC discovery URL, with the bound issuer set to the same value. The issuer string must match exactly what GitLab advertises, because a trailing slash or hostname mismatch is a common reason logins fail. Read the auth configuration back to confirm the issuer and discovery URL match your instance. Then attempt a login with no role attached, which should return a clear error instead of a token.

  3. Create a test secret path

    The secrets owner confirms a KV version 2 secrets engine is available, then writes a dummy value under a path reserved for one non-production GitLab project, such as a sandbox project with no deployment rights. Production secrets stay out of this exercise entirely. Read the secret back as an administrator to confirm the path and version. Share only the path with the pipeline team, never the value, and keep the value out of tickets, chat and job logs.

  4. Write a read-only policy for that path

    The Vault administrator writes a policy that grants only the read capability on the exact data path of the test secret, with no list, create, update or delete capabilities. Keep one policy per application or environment, so a compromised job cannot reach unrelated secrets. Confirm the policy by testing it with a token that carries it: a read on the test path should succeed, and a write to the same path should return permission denied.

  5. Bind the role to project and branch

    Create a JWT role that accepts only tokens issued for the test project, using claims that GitLab sets for each job. Set the user claim to a stable identifier, bind the audience to the value the job will request, and restrict the ref and protected-branch claims so only a protected branch can receive the policy. Attach the read-only policy and a short token lifetime. Confirm by reading the role back and checking each bound claim against a sample token from a protected branch and from an unprotected branch.

  6. Request the ID token in the job

    The pipeline author adds an id_tokens entry to the test job with the audience that Vault expects, then adds a secrets entry that names the Vault path and role and maps the value to a CI/CD variable. Keep the audience specific to this Vault instance. Restrict who can edit the job definition on protected branches, since the role and path are visible to anyone who can change the pipeline. Run the job on a protected branch and confirm in the job log that the secrets step completed without an error.

  7. Confirm retrieval without printing the value

    In the test job, print only a fixed success message and never the secret value or any part of it, then confirm the job passes. Next, open the Vault audit log and find the JWT login entry, which should name the role and show the project and branch claims without any secret content. Finally, run the same pipeline from an unprotected branch and confirm Vault denies the login. A denial there proves the branch restriction works.

Which GitLab claims map to which Vault role conditions

This table pairs each GitLab ID token claim with the Vault role condition that can match it, the stability of that claim, and the policy paths it should unlock. Read the stability column first, because a role condition built on an unstable claim can stop matching after a rename or quietly widen access. Confirm the exact claim names and their formats in the GitLab CI/CD ID token documentation for your installed version before writing any role.

GitLab claimVault role conditionStability and whyPolicy path use
project_idbound_claims on project_idStable. The numeric ID persists through project renames and path changes.Anchor production secret access to this value.
project_pathbound_claims on project_pathUnstable. Changes on rename or move, so the role can stop matching without warning.Use as a readable check alongside project_id, not alone.
namespace_idbound_claims on namespace_idStable. Identifies the group or user namespace that owns the project.Grant shared secret paths to a whole group where teams share ownership.
namespace_pathbound_claims on namespace_pathUnstable. Changes when a group is renamed or the subgroup structure changes.Avoid as the only condition for group-wide read access.
refbound_claims on refWeak alone. Anyone with push rights can create a branch with a matching name.Do not map deploy or production paths to ref alone.
ref_protectedbound_claims on ref_protected set to trueStronger. GitLab sets it from branch or tag protection rules, which maintainers control.Map production secret paths only to protected refs.
ref_typebound_claims on ref_typeDistinguishes branch from tag pipelines. Stable for a given ref kind.Restrict release secrets to tag pipelines where your process uses tags.
pipeline_sourcebound_claims on pipeline_sourceReflects what triggered the job, such as a push, merge request or schedule. Confirm the value list.Keep deploy secrets away from merge request and external-trigger pipelines.
subbound_subjectBuilt from project and ref details, so it changes when either changes. Confirm its format for your version.Use only when the format is confirmed and tested against a sample job.

Hardening the GitLab to Vault trust path

The trust path runs from a GitLab job's ID token to a Vault role and then to the secret the job reads. Each link can be tightened separately, and the cards below cover the controls that matter most. Roll them out gradually, starting with role conditions and logging, as the GitLab hardening guidance recommends.

Bind Vault roles to protected refs and project IDs

Condition each Vault role on the ID token claims that identify the exact project and ref allowed to authenticate, such as the project ID in the project_id claim and the protected branch in ref_protected and ref. Matching on project path alone is weaker because paths change when projects are renamed or moved. Check that unprotected branches and merge request pipelines cannot satisfy the role conditions, and test this with a deliberately mismatched job before go-live.

Use one Vault role per environment

Create separate roles for development, staging and production, each granting read access only to the secret paths that environment needs. A single broad role lets a compromised lower-environment job read production credentials. Ask your team to list which roles can read each production path and confirm that no shared role reaches it.

Keep token and lease lifetimes short

Set short time-to-live values on the Vault tokens issued to jobs so that a leaked token expires soon after the job finishes. Pair this with short-lived dynamic credentials where the target system supports them. Confirm the exact lifetime settings in the Vault and OpenBao documentation for your version, and align them with how long your longest job runs.

Prevent secrets from appearing in job logs

Mark CI/CD variables that hold secrets as masked and, where the value format allows it, as protected so they reach only protected branches. Avoid commands that print credentials, such as debug tracing with set -x around authentication steps. Review job logs after a test run to confirm that the secret values do not appear, because masking only hides values that match exactly.

Audit Vault access and alert on failures

Enable Vault audit devices so every authentication and secret read is recorded, and forward those entries to your log platform. Create alerts for repeated JWT login failures, logins from unexpected projects, and reads of production paths outside deployment windows. Decide in advance who receives each alert and what response is expected.

Manage Vault like production secrets

Place the Vault cluster, its configuration, its storage backend and its unseal or auto-unseal keys under the same change control, backup and access review as other production secrets. Changes to JWT auth configuration and role conditions should go through review and be logged. Confirm with your Vault or OpenBao provider how the chosen storage backend is backed up and restored.

Choosing between HashiCorp Vault and OpenBao for this integration

Vault Community Edition is distributed as binaries or source and gives you the Vault API that GitLab's ID token authentication can call. The license has changed over time. Releases from 1.15.0 onward fall under the Business Source License 1.1, and the Vault LICENSE file names IBM as licensor. That license bars competitive hosted or embedded offerings, so legal review should confirm whether your use is affected.

  • License conversion: Each release converts to MPL-2.0 four years after publication, so a pinned older version will move to a more permissive license on a known schedule.
  • Feature set: Community Edition omits the Enterprise features in HashiCorp's documentation, including namespaces, Enterprise MFA, control groups and performance replication.
  • Role options: Confirm in the Community Edition documentation that the JWT auth role options you need, such as bound claims and token policies, exist in the build you will run.
  • Support model: Without a HashiCorp support contract, your team or partner owns upgrades, patching and incident response.
  • Current release: The GitHub releases page lists Vault 2.2.0 (October 15, 2026) as current, so decide which release line you will standardize on.

Before you build, confirm the JWT role options, the auth path and the exact Community Edition version against the GitLab steps in this guide.

Why the secret fetch fails and how to fix it

This table lists the failures teams most often hit when a CI/CD job pulls a secret from Vault, with the likely cause and a check you can run to confirm and fix it. Work down the rows in the order a job reaches each step: tier and feature, token, Vault authentication, network trust, token lifetime and finally the policy. The main takeaway is that most failures trace back to a mismatch between the job's ID token claims and the Vault role, so compare those values first.

FailureLikely causeFix to verify
Secret fetch is not availablePremium tier or the secrets feature is not enabled for the project or groupConfirm the tier on the instance and check that the secrets keyword is accepted in a test pipeline on the same project
Authentication rejected with audience errorThe aud claim in the job's ID token does not match the audience set on the Vault JWT auth methodPrint the decoded token claims in a debug job, then set the same audience value in both the CI/CD id_tokens block and the Vault role
Role lookup fails after a project renameBound claims in the Vault role still use the old project path, which no longer matches the sub or project_path claimReplace path-based bound claims with the numeric project ID, then rerun the job on the renamed project
Merge request pipeline cannot read the secretThe Vault role is bound to a protected branch, and merge request pipelines do not run on that refConfirm the ref claim in a merge request pipeline, then scope the role to the merge request ref or a separate role with a narrower policy
Runner cannot verify Vault certificateThe runner does not trust the internal certificate authority that signed the Vault listener certificateAdd the CA bundle to the runner's trust store or to the job's CA settings, then run vault status from the runner host
Token expires during a long jobThe Vault token lifetime is shorter than the job runtime and the token is not renewedCheck the token TTL and max TTL on the role, then fetch secrets early in the job or split long steps into separate jobs
Permission denied on a KV v2 secretThe policy grants read on the secret metadata path but not on the data path under the mountAdd read capability on the path with the data segment for the KV v2 mount, then test with vault kv get as the job role

Questions buyers ask before connecting GitLab to Vault

What does it cost to connect GitLab CI/CD to HashiCorp Vault?

The cost has three parts: GitLab, Vault and the people doing the work. GitLab's pricing page lists Premium at 29 US dollars per user per month billed annually, and the external secrets feature is part of the Premium tier, so every user who needs it is counted under that subscription. Vault Enterprise pricing is not published, so request a quote from HashiCorp sales for the license, and confirm whether you are buying self-managed Enterprise, pay-as-you-go or HCP Vault Dedicated. Services fees depend on the written scope. Ask any partner to separate one-time build work from recurring support, upgrades and Vault or OpenBao operations.

How long does a Vault integration for GitLab CI/CD take?

Duration depends on how many projects and secret paths you need, how many Vault auth roles must be designed, whether Vault already exists, and how your network and TLS certificates are set up. A pilot with one project and one secret is much shorter than a rollout across groups with branch-specific access. No duration is quoted here. Ask for a written plan with phases, the environments included and the acceptance tests, and confirm which items depend on your security review or change windows.

Can our own team connect GitLab CI/CD to Vault without a partner?

Yes, if your team already runs Vault or OpenBao, administers GitLab at the Premium tier or higher, and has someone who understands JWT claims and Vault policies. The work is mostly configuration: enabling ID tokens in CI/CD, defining a Vault JWT or OIDC auth method, writing roles that match the project and branch claims, and testing that jobs receive only their secrets. Teams without that mix often benefit from a fixed-scope engagement, where scope and deliverables are agreed in writing before work starts.

What do our security and platform teams need to provide?

Plan for time from both teams. Security should define which secrets belong in Vault, who approves access, how long leases last and how audit logs are reviewed. Platform engineering should provide the Vault and GitLab endpoints, network paths from runners to Vault, TLS certificates, storage backend decisions and the change process for the Vault policies. Both teams should agree on a naming scheme for roles, paths and projects, because stable project identifiers and branch rules are hard to change after jobs depend on them.

Should we use HashiCorp Vault or OpenBao under our license policy?

Choose based on your license rules, support needs and the Vault features you depend on. Vault's LICENSE uses the Business Source License 1.1 for releases from 1.15.0 onward, bars competitive hosted or embedded offerings and converts each version to MPL-2.0 four years after publication. OpenBao is a community fork under MPL-2.0 managed under the Linux Foundation's OpenSSF. Its migration guide was tested only against Vault 1.14.1 community edition, so confirm compatibility with any Enterprise features you use before choosing it. Ask your legal team to review both licenses against your intended use.

How do we handle upgrades on the GitLab side and the Vault side?

Treat them as two tracks with a shared test plan. On GitLab, follow the required upgrade stops, since the upgrade paths documentation lists x.2, x.5, x.8 and x.11 as required stops from GitLab 17.5 onward, and confirm that the CI/CD secrets settings still behave as expected after each step. On Vault or OpenBao, confirm the release notes, storage backend compatibility and any Enterprise license implications before moving. Keep a staging Vault and a test GitLab project so role claims and policy changes are tested before production jobs depend on them.

What goes wrong most often when GitLab jobs read secrets from Vault?

The most common failures are role conditions that are too broad, claims that do not match after a project rename, runners that cannot reach Vault and secrets exposed in job logs. Restrict roles by the project path or stable project ID and by protected branches, use short token lifetimes, and test a job from an unprotected branch to confirm it is refused. Check your runner network path and TLS trust early, and confirm that masked variables and job output rules are set so secret values do not appear in logs.

How DeployOpen scopes and delivers this integration

DeployOpen plans and builds the link between self-managed GitLab and your HashiCorp Vault or OpenBao cluster inside your own infrastructure. We agree the projects, branches, roles and secret paths in writing before any configuration starts, then test and document the result so your team can run it.

How we scope

  • Inventory of the projects and groups that need Vault secrets, and the environments each one deploys to
  • Written mapping of each secret path to a Vault role bound to project and branch claims
  • Confirmation of your GitLab tier, Vault edition and the Vault version your cluster runs before design starts
  • Agreed list of out-of-scope items, such as dynamic database credentials or Vault cluster hardening

How we build

  • Configure JWT authentication in Vault using the ID token claims GitLab issues for each job
  • Create least-privilege Vault roles and policies that limit access by project path and protected branch
  • Wire CI/CD jobs to fetch secrets at runtime, with masked variables and no long-lived copies in GitLab
  • Test success and denied cases on protected and unprotected branches, and record the results

How we hand over

  • Runbook covering role creation, claim conditions, secret rotation steps and how to add a new project
  • Configuration as code for the Vault auth method, roles and policies, stored in your repositories
  • Walkthrough with your platform and security teams, plus a record of tests and any open risks
  • Documentation of the Premium tier requirement and the GitLab and Vault versions tested

How we support

  • Ongoing support, upgrades and testing are available as a retainer after handover
  • Review of role conditions when projects are renamed, moved or added to groups
  • Verification after GitLab and Vault upgrades, checked against the version stops and releases you plan to use
  • Quotes follow from the written scope, and changes to scope are agreed in writing before work starts

Sources and further reading

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.