Skip to main content
This guide explains how to configure Workload Identity for GitLab CI/CD to authenticate with Bytebase without storing long-lived credentials.

Step 1: Create a Workload Identity in Bytebase

  1. Go to IAM & Admin > Users & Groups.
  2. Click Add User in the upper-right corner.
  3. Select Workload Identity as the Type.
  4. Fill in the configuration:
FieldDescriptionExample
NameDisplay name for this identityGitLab Deploy
EmailUnique email prefix (automatically appended with @workload.bytebase.com)gitlab-deploy
PlatformSelect GitLab CIGitLab CI
Group / UsernameGitLab group or username (required)my-group
ProjectProject name (leave empty to allow all projects)my-project
Allowed Branches/TagsSelect branch/tag restrictionsAll branches and tags
RolesAssign workspace rolesGitOps Service Agent
  1. Click Confirm to create the Workload Identity.

Step 2: Assign Roles

After creating the Workload Identity, assign the GitOps Service Agent role to enable automated CI/CD workflows:
  1. Go to your project’s Settings > Members.
  2. Click Grant Access.
  3. Enter the Workload Identity email (e.g., [email protected]).
  4. Select the GitOps Service Agent role.
  5. Click Confirm.
The GitOps Service Agent role is designed for automated CI/CD workflows, allowing the identity to create and execute database changes. See Roles and Permissions for details.

Step 3: Configure GitLab CI/CD Pipeline

In your GitLab CI/CD pipeline, add the following configuration:

Request OIDC Token

Add id_tokens configuration to get the JWT token from GitLab:
stages:
  - deploy

deploy-database:
  stage: deploy
  image: alpine:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  variables:
    BYTEBASE_URL: https://bytebase.example.com
    WORKLOAD_IDENTITY_EMAIL: [email protected]
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      # Exchange GitLab OIDC token for Bytebase API token
      RESPONSE=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}")

      ACCESS_TOKEN=$(echo $RESPONSE | jq -r '.accessToken')
      if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
        echo "Failed to get access token"
        echo $RESPONSE
        exit 1
      fi

      # Verify the token by calling the user info API
      USER_INFO=$(curl -s "${BYTEBASE_URL}/v1/users/me" \
        -H "Authorization: Bearer $ACCESS_TOKEN")
      echo "Authenticated as: $USER_INFO"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Complete Example

Here’s a complete GitOps workflow that uses Workload Identity to deploy database migrations:
# .gitlab-ci.yml
stages:
  - review
  - deploy

variables:
  BYTEBASE_URL: https://bytebase.example.com
  WORKLOAD_IDENTITY_EMAIL: [email protected]

# SQL Review on merge requests
sql-review:
  stage: review
  image: bytebase/sql-review-action:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - |
      # Exchange OIDC token for Bytebase token
      export BYTEBASE_TOKEN=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}" \
        | jq -r '.accessToken')

      # Run SQL review
      sql-review --url ${BYTEBASE_URL} --token ${BYTEBASE_TOKEN} \
        --file-pattern "migrations/**/*.sql"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - migrations/**

# Deploy on merge to main
rollout:
  stage: deploy
  image: bytebase/bytebase-action:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - |
      # Exchange OIDC token for Bytebase token
      export BYTEBASE_TOKEN=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}" \
        | jq -r '.accessToken')

      # Create release and rollout
      bytebase-action rollout \
        --url ${BYTEBASE_URL} \
        --token ${BYTEBASE_TOKEN} \
        --file-pattern "migrations/**/*.sql" \
        --project projects/my-project \
        --targets instances/prod/databases/mydb
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      changes:
        - migrations/**
For more details on GitOps workflows, see GitOps Overview and Migration-Based Workflow.

Self-Hosted GitLab

For self-hosted GitLab instances, update the audience (aud) to match your GitLab instance URL:
id_tokens:
  GITLAB_OIDC_TOKEN:
    aud: https://gitlab.your-company.com
When creating the Workload Identity in Bytebase, ensure the configuration matches your self-hosted GitLab instance.

Troubleshooting

Token Exchange Fails

If the token exchange returns an error:
  1. Verify the project path and branch: Check that your pipeline’s project path and branch match the configured values in Bytebase.
  2. Check the audience: Ensure the aud in your id_tokens configuration matches your GitLab instance URL (e.g., https://gitlab.com for GitLab.com).
  3. Verify OIDC is enabled: GitLab CI/CD OIDC tokens require GitLab 15.7 or later.

Permission Denied

If API calls return permission errors:
  1. Verify the Workload Identity has the GitOps Service Agent role assigned.
  2. Check that the Workload Identity is a member of the target project.

Debug Token Claims

To inspect the OIDC token claims, decode the JWT:
script:
  - |
    echo "$GITLAB_OIDC_TOKEN" | cut -d. -f2 | base64 -d | jq .
This shows the token’s claims including sub, aud, namespace_path, project_path, and ref that Bytebase validates.