Skip to content

GitLab Provider

The GitLab provider enables SecretZero to store and manage secrets in GitLab CI/CD variables. It supports token authentication for managing both project-level and group-level variables, with options for environment scoping, masking, and protection.

Overview

The GitLab provider is ideal for:

  • CI/CD pipelines using GitLab CI/CD workflows
  • Multi-project deployments requiring consistent secrets across repositories
  • Environment-specific deployments (production, staging, development)
  • Group-level secret sharing across multiple projects in a GitLab group
  • Self-hosted GitLab instances with custom URLs
  • Teams using GitLab Enterprise with advanced CI/CD features

Supported Target Types

Target Type Description Use Case
gitlab_variable GitLab CI/CD project variable Project-specific credentials, API keys, environment variables
gitlab_group_variable GitLab CI/CD group variable Shared secrets across all projects in a GitLab group

Authentication

Token Authentication

The GitLab provider uses Personal Access Tokens (PAT) or OAuth tokens for authentication.

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

When to use: All scenarios with GitLab CI/CD variable management.

Creating a Personal Access Token

  1. Navigate to GitLab Settings:
  2. Go to GitLab → User Settings → Access Tokens
  3. Or visit: https://gitlab.com/-/profile/personal_access_tokens

  4. Create a new token:

  5. Click "Add new token"
  6. Give it a descriptive name (e.g., "SecretZero Token")
  7. Set expiration (recommended: 90 days with rotation)

  8. Select scopes:

  9. api - Full API access (required for reading/writing variables)

  10. Generate and save token:

  11. Click "Create personal access token"
  12. Copy the token immediately (it won't be shown again)
  13. Token format: glpat-xxxxxxxxxxxxxxxxxxxxx

  14. Set environment variable:

    export GITLAB_TOKEN=glpat-your_token_here
    

Self-Hosted GitLab Support

For self-hosted GitLab instances, specify a custom URL:

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}
        url: https://gitlab.mycompany.com

Note: The URL should be the base URL without /api/v4 - the provider handles API path construction.

Configuration

Basic Configuration

version: '1.0'

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  - name: api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: mygroup/myproject
          masked: true
          protected: false

Multi-Project Configuration

Deploy secrets to multiple projects:

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  - name: shared_api_key
    kind: random_string
    config:
      length: 32
    targets:
      # Backend project
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/backend-service
          masked: true

      # Frontend project
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/frontend-app
          masked: true

      # Worker project
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/worker-service
          masked: true

Group-Level Configuration

Share secrets across all projects in a group:

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  - name: shared_secret
    kind: random_string
    config:
      length: 64
    targets:
      # Group-level variable (available to all projects in group)
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: myorganization
          masked: true
          protected: false
          environment_scope: "*"

Environment-Specific Configuration

Use environment scopes for production, staging, and development:

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  - name: database_password
    kind: random_password
    config:
      length: 32
      special: true
    targets:
      # Production environment
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: true
          masked: true

      # Staging environment
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: staging
          protected: true
          masked: true

      # Development environment (all branches)
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: development
          protected: false
          masked: true

Target Types

GitLab Project Variable

The gitlab_variable target type stores secrets in GitLab CI/CD project variables.

Configuration Options

Option Type Required Default Description
project string Yes - Project path (user/project or group/project) or numeric ID
environment_scope string No * Environment scope (e.g., production, staging, * for all)
protected boolean No false Only available on protected branches/tags
masked boolean No true Hidden in job logs
variable_type string No env_var Variable type: env_var or file

GitLab Group Variable

The gitlab_group_variable target type stores secrets in GitLab CI/CD group variables, making them available to all projects in the group.

Configuration Options

Option Type Required Default Description
group string Yes - Group path or numeric ID
environment_scope string No * Environment scope (e.g., production, staging, * for all)
protected boolean No false Only available on protected branches/tags
masked boolean No true Hidden in job logs
variable_type string No env_var Variable type: env_var or file

Example: Project Variable

secrets:
  - name: api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorganization/myproject
          masked: true
          protected: false

The variable will be available in CI/CD as $API_KEY.

Example: Custom Variable Name

SecretZero converts secret names to uppercase for GitLab variables. The secret_name parameter in the YAML becomes the key field in GitLab.

secrets:
  - name: database_password
    kind: random_password
    config:
      length: 32
      special: true
      exclude_characters: '"@/\`'
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          masked: true
          protected: true

The variable will be available as $DATABASE_PASSWORD.

Example: Group Variable

secrets:
  - name: shared_api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: myorganization
          masked: true
          environment_scope: "*"

Available to all projects in the group as $SHARED_API_KEY.

Example: Environment-Scoped Variable

secrets:
  - name: database_url
    kind: static
    config:
      default: postgresql://prod-db.example.com:5432/myapp
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: true

Only available in production environment jobs.

Example: File Variable

secrets:
  - name: service_account_key
    kind: static
    config:
      default: |
        {
          "type": "service_account",
          "project_id": "my-project",
          "private_key": "-----BEGIN PRIVATE KEY-----\n..."
        }
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          variable_type: file
          masked: false

The variable value is written to a temporary file, and $SERVICE_ACCOUNT_KEY contains the file path.

Example: Protected Variable

secrets:
  - name: prod_deploy_key
    kind: random_string
    config:
      length: 64
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          protected: true
          masked: true
          environment_scope: production

Only available on protected branches and tags in production environment.

Complete Examples

Example 1: Simple Application Secrets

version: '1.0'

variables:
  gitlab_project: myorganization/myapp
  environment: production

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  # API Key
  - name: api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          masked: true
          protected: false

  # Database Password
  - name: database_password
    kind: random_password
    rotation_period: 90d
    config:
      length: 32
      special: true
      exclude_characters: '"@/\`'
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          environment_scope: "{{var.environment}}"
          masked: true
          protected: true

  # JWT Signing Secret
  - name: jwt_secret
    kind: random_string
    config:
      length: 64
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          masked: true

metadata:
  project: "{{var.gitlab_project}}"
  owner: backend-team

Example 2: Multi-Environment Setup

version: '1.0'

variables:
  gitlab_project: myorganization/myapp

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  # Database credentials for all environments
  - name: database_password
    kind: random_password
    config:
      length: 32
      special: true
      exclude_characters: '"@/\`'
    targets:
      # Production environment
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          environment_scope: production
          protected: true
          masked: true

      # Staging environment
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          environment_scope: staging
          protected: true
          masked: true

      # Development environment
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          environment_scope: development
          protected: false
          masked: true

  # API endpoints (environment-specific static values)
  - name: api_url_prod
    kind: static
    config:
      default: https://api.example.com
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          variable_key: API_URL
          environment_scope: production

  - name: api_url_staging
    kind: static
    config:
      default: https://staging-api.example.com
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_project}}"
          variable_key: API_URL
          environment_scope: staging

Example 3: Group-Level Shared Secrets

version: '1.0'

variables:
  gitlab_group: myorganization

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  # Shared API key across all projects
  - name: external_api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: "{{var.gitlab_group}}"
          masked: true
          protected: false
          environment_scope: "*"

  # Shared authentication token
  - name: auth_token
    kind: random_string
    config:
      length: 64
    targets:
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: "{{var.gitlab_group}}"
          masked: true
          environment_scope: "*"

  # Service account credentials (file type)
  - name: service_account
    kind: static
    config:
      default: |
        {
          "type": "service_account",
          "project_id": "shared-project"
        }
    targets:
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: "{{var.gitlab_group}}"
          variable_type: file
          masked: false

Example 4: Multi-Project Deployment

version: '1.0'

variables:
  gitlab_org: myorganization

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  # Shared API key across specific services
  - name: external_api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      # Backend service
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_org}}/backend-service"
          environment_scope: production
          protected: true
          masked: true

      # Frontend app
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_org}}/frontend-app"
          environment_scope: production
          protected: true
          masked: true

      # Worker service
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_org}}/worker-service"
          environment_scope: production
          protected: true
          masked: true

  # Service-specific secrets
  - name: database_password
    kind: random_password
    config:
      length: 32
      special: true
    targets:
      # Only backend needs database access
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_org}}/backend-service"
          environment_scope: production
          protected: true
          masked: true

      # Worker also needs database
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: "{{var.gitlab_org}}/worker-service"
          environment_scope: production
          protected: true
          masked: true

Example 5: Protected and Masked Variables

version: '1.0'

providers:
  gitlab:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_TOKEN}

secrets:
  # Production secret - protected and masked
  - name: prod_api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: true  # Only on protected branches
          masked: true     # Hidden in logs

  # Staging secret - protected but visible
  - name: staging_api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: staging
          protected: true
          masked: false  # Visible in logs for debugging

  # Development secret - unprotected
  - name: dev_api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: development
          protected: false  # Available on all branches
          masked: true

Token Permissions and Scopes

Required Token Scopes

For All Variables

  • api - Full API access
  • Grants access to create, update, and delete CI/CD variables
  • Required for both project-level and group-level variables
  • Includes read and write permissions

Token Best Practices

  1. Use project/group access tokens when available:
  2. Limit token scope to specific projects or groups
  3. Set shorter expiration periods
  4. Revoke tokens after use in automated workflows

  5. Rotate tokens regularly:

    # Document token expiration
    metadata:
      token_expires: 2024-12-31
      token_owner: devops-team
    

  6. Use different tokens for different purposes:

  7. Separate tokens for production and non-production
  8. Different tokens per team or project group
  9. Dedicated tokens for automation

  10. Audit token usage:

  11. Review token activity in GitLab Settings → Access Tokens
  12. Monitor API rate limits
  13. Track variable updates through lockfile

Required Permissions

For Project Variables

  • Maintainer role or higher on the project
  • Required permissions:
  • Read project settings
  • Manage CI/CD variables

For Group Variables

  • Owner role or higher on the group
  • Required permissions:
  • Manage group CI/CD variables
  • Access to all projects in group

Integration with GitLab CI/CD Pipelines

Using Variables in Pipelines

Once synced, variables are available in your .gitlab-ci.yml:

Basic Usage

# .gitlab-ci.yml
deploy:
  stage: deploy
  script:
    - echo "API Key: $API_KEY"
    - echo "Database Password: $DATABASE_PASSWORD"
    - ./deploy.sh
  environment:
    name: production

Environment-Specific Usage

# .gitlab-ci.yml
stages:
  - test
  - deploy

deploy_staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
    - echo "Database: $DATABASE_PASSWORD"
    - ./deploy.sh staging
  environment:
    name: staging
  only:
    - develop

deploy_production:
  stage: deploy
  script:
    - echo "Deploying to production..."
    - echo "Database: $DATABASE_PASSWORD"
    - ./deploy.sh production
  environment:
    name: production
  only:
    - main

File Variables Usage

# .gitlab-ci.yml
deploy:
  stage: deploy
  script:
    # File variables are available as paths
    - cat $SERVICE_ACCOUNT_KEY
    - gcloud auth activate-service-account --key-file=$SERVICE_ACCOUNT_KEY
    - ./deploy.sh
  environment:
    name: production

Protected Variables Usage

# .gitlab-ci.yml
deploy_prod:
  stage: deploy
  script:
    # Protected variables only available on protected branches
    - echo "Production deployment key: $PROD_DEPLOY_KEY"
    - ./deploy.sh production
  environment:
    name: production
  only:
    - main  # Must be a protected branch

Manual Jobs with Approval

Combine protected variables with manual jobs:

# .gitlab-ci.yml
deploy_staging:
  stage: deploy
  script:
    - ./deploy.sh staging
  environment:
    name: staging
    action: start
  only:
    - develop

deploy_production:
  stage: deploy
  script:
    - ./deploy.sh production
  environment:
    name: production
    action: start
  when: manual  # Requires manual trigger
  only:
    - main
  # Uses protected variables (only available on main)

Multi-Environment Pipeline

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  # Project-level variables (non-scoped)
  APP_NAME: myapp

build:
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/

test:
  stage: test
  script:
    - npm run test

deploy_dev:
  stage: deploy
  script:
    # Development-scoped variables
    - echo "API URL: $API_URL"
    - echo "Database: $DATABASE_PASSWORD"
    - ./deploy.sh
  environment:
    name: development
  only:
    - develop

deploy_staging:
  stage: deploy
  script:
    # Staging-scoped variables
    - echo "API URL: $API_URL"
    - echo "Database: $DATABASE_PASSWORD"
    - ./deploy.sh
  environment:
    name: staging
  only:
    - develop
  when: manual

deploy_prod:
  stage: deploy
  script:
    # Production-scoped variables (protected)
    - echo "API URL: $API_URL"
    - echo "Database: $DATABASE_PASSWORD"
    - ./deploy.sh
  environment:
    name: production
  only:
    - main
  when: manual

Best Practices

1. Use Group Variables for Shared Secrets

# Good: Share common secrets at group level
secrets:
  - name: shared_api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_group_variable
        config:
          group: myorganization
          masked: true

# Avoid: Duplicating same secret across multiple projects
secrets:
  - name: shared_api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/project1
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/project2
      # ... repeated for many projects

2. Mask Sensitive Variables

# Good: Mask sensitive data
secrets:
  - name: api_key
    kind: random_string
    config:
      length: 32
      charset: alphanumeric  # Ensures maskable format
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          masked: true  # Hidden in logs

# Avoid: Leaving sensitive data unmasked
secrets:
  - name: api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          masked: false  # Visible in logs

3. Use Protected Variables for Production

# Good: Protect production secrets
secrets:
  - name: prod_db_password
    kind: random_password
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: true  # Only on protected branches
          masked: true

# Avoid: Unprotected production secrets
secrets:
  - name: prod_db_password
    kind: random_password
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: false  # Available on all branches

4. Use Environment Scopes

# Good: Scope variables to environments
secrets:
  - name: database_password
    kind: random_password
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: production
          protected: true

# Avoid: Global scope for environment-specific secrets
secrets:
  - name: prod_database_password  # Name indicates environment
    kind: random_password
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          environment_scope: "*"  # Available to all environments

5. Rotate Tokens Regularly

secrets:
  - name: api_key
    kind: random_string
    rotation_period: 90d  # Automatic rotation
    config:
      length: 32
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          masked: true

6. Use the Lockfile

# Generate lockfile on first sync
secretzero sync -f Secretfile.yml

# Commit lockfile to track secret lifecycle
git add .secretzero.lock
git commit -m "Update secret lockfile"

# Verify lockfile before syncing
secretzero sync -f Secretfile.yml --verify-lock

7. Implement Naming Conventions

# Good: Clear, consistent naming
secrets:
  - name: database_password      # Becomes DATABASE_PASSWORD
  - name: api_external_key       # Becomes API_EXTERNAL_KEY
  - name: jwt_signing_secret     # Becomes JWT_SIGNING_SECRET

# Avoid: Ambiguous names
secrets:
  - name: secret1
  - name: key
  - name: password

8. Audit Variable Access

# Use GitLab audit logs to track variable access
# Project Settings → Audit Events → Filter by "variable"

# Monitor variable updates with lockfile
git log .secretzero.lock

# Review CI/CD job logs for variable usage
# CI/CD → Pipelines → Select pipeline → Review jobs

9. Never Commit Tokens

# Add to .gitignore
echo "GITLAB_TOKEN" >> .gitignore
echo ".env" >> .gitignore
echo "*.secret" >> .gitignore

# Scan for accidentally committed secrets
git log -p | grep -i "glpat-"

10. Use File Variables for Complex Data

# Good: Use file variables for JSON/credentials
secrets:
  - name: service_account_key
    kind: static
    config:
      default: |
        {
          "type": "service_account",
          "private_key": "..."
        }
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          variable_type: file  # Writes to temp file
          masked: false  # JSON cannot be masked

# Avoid: Using env_var for large/complex data
secrets:
  - name: service_account_key
    kind: static
    config:
      default: '{"type":"service_account","private_key":"..."}'
    targets:
      - provider: gitlab
        kind: gitlab_variable
        config:
          project: myorg/myapp
          variable_type: env_var  # Complex JSON in env var

Troubleshooting

Authentication Failed

Error: GitLab authentication failed

Solutions:

  1. Verify token is set:

    echo $GITLAB_TOKEN
    

  2. Test token with GitLab API:

    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/user"
    

  3. Check token hasn't expired:

  4. Go to GitLab → User Settings → Access Tokens
  5. Verify token expiration date

  6. Verify token scopes:

    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/user" | jq .
    

  7. For self-hosted GitLab, verify URL:

    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.mycompany.com/api/v4/user"
    

  8. Test with SecretZero:

    secretzero test -f Secretfile.yml
    

Permission Denied

Error: 403 Forbidden or Insufficient permissions

Solutions:

  1. For project variables, ensure Maintainer role:
  2. Go to Project → Members
  3. Verify you have at least Maintainer role
  4. Token owner must have appropriate access

  5. For group variables, ensure Owner role:

  6. Go to Group → Members
  7. Verify you have Owner role
  8. Group variables require higher permissions

  9. Test project access:

    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/projects/myorg%2Fmyproject"
    

  10. Test group access:

    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/groups/myorg"
    

  11. Verify token has api scope:

  12. Recreate token with api scope if missing

Variable Not Appearing in Pipeline

Error: Variable is synced but not available in CI/CD jobs

Solutions:

  1. Verify variable name (case-sensitive):

    # If secret name in SecretZero is: api_key
    # It becomes in GitLab: API_KEY (uppercase with underscores)
    
    # In pipeline, use exact uppercase name:
    script:
      - echo $API_KEY  # Correct
      - echo $api_key  # Wrong - won't work
    

  2. Check environment scope:

    # Variable scoped to 'production'
    deploy:
      script:
        - echo $DATABASE_PASSWORD
      environment:
        name: production  # Must match scope
    

  3. Verify protected variable settings:

    # Protected variable on main branch
    deploy:
      script:
        - echo $PROD_API_KEY
      only:
        - main  # Must be protected branch
    

  4. Check variable in GitLab UI:

  5. Project Settings → CI/CD → Variables
  6. Verify variable exists and name matches
  7. Check environment scope settings

  8. Review pipeline job logs:

  9. Look for variable expansion errors
  10. Check if job has access to environment

Invalid Characters in Variable Names

Error: Invalid variable name or Variable name contains invalid characters

Solutions:

  1. Use valid characters only (alphanumeric and underscores):

    # Good: Valid variable names
    secrets:
      - name: api_key           # Valid
      - name: database_password  # Valid
      - name: jwt_secret_key     # Valid
    
    # Bad: Invalid characters
    secrets:
      - name: api-key           # Hyphens not allowed
      - name: api.key           # Dots not allowed
      - name: api key           # Spaces not allowed
    

  2. Use custom variable names:

    secrets:
      - name: my-api-key  # Name with hyphens
        kind: random_string
        config:
          length: 32
        targets:
          - provider: gitlab
            kind: gitlab_variable
            config:
              project: myorg/myapp
              variable_key: MY_API_KEY  # Valid custom name
    

Masked Variable Rejected

Error: Variable value does not meet masking requirements

Solutions:

  1. Ensure value is at least 8 characters:

    # Good: Long enough to mask
    secrets:
      - name: api_key
        kind: random_string
        config:
          length: 32  # Minimum 8 characters
        targets:
          - provider: gitlab
            kind: gitlab_variable
            config:
              project: myorg/myapp
              masked: true
    

  2. Remove special characters:

    # Good: Alphanumeric only (maskable)
    secrets:
      - name: api_key
        kind: random_string
        config:
          length: 32
          charset: alphanumeric  # No special chars
        targets:
          - provider: gitlab
            kind: gitlab_variable
            config:
              project: myorg/myapp
              masked: true
    
    # Bad: Special characters prevent masking
    secrets:
      - name: api_key
        kind: random_string
        config:
          length: 32
          charset: all  # Includes special chars
        targets:
          - provider: gitlab
            kind: gitlab_variable
            config:
              project: myorg/myapp
              masked: true  # Will fail
    

  3. Use file variables for complex data:

    # Good: File variables don't require masking
    secrets:
      - name: service_account_key
        kind: static
        config:
          default: '{"key": "value with spaces"}'
        targets:
          - provider: gitlab
            kind: gitlab_variable
            config:
              project: myorg/myapp
              variable_type: file
              masked: false
    

  4. Masked variable requirements:

  5. Minimum 8 characters
  6. No spaces
  7. No special regex characters
  8. Must match pattern: ^[a-zA-Z0-9_+=/@:.~-]{8,}$

Project or Group Not Found

Error: 404 Project Not Found or 404 Group Not Found

Solutions:

  1. Verify project path:

    # Check project exists and path is correct
    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/projects/myorg%2Fmyproject"
    

  2. Use URL encoding for special characters:

    # Project path with special characters
    config:
      project: myorg/my-project  # Hyphen is OK
      # NOT: myorg/my project  # Space not allowed
    

  3. Use numeric project ID:

    # Alternative: Use numeric ID instead of path
    config:
      project: "12345678"  # Numeric project ID
    

  4. Verify group path:

    # Check group exists
    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/groups/myorg"
    

  5. Check access permissions:

  6. Verify you have access to the project/group
  7. Token owner must be a member

Rate Limits

Error: 429 Too Many Requests or Rate limit exceeded

Solutions:

  1. Check current rate limit:

    curl -I --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
      "https://gitlab.com/api/v4/user" | grep RateLimit
    

  2. Authenticated requests have higher limits:

  3. Unauthenticated: 10 requests/minute (per IP)
  4. Authenticated: 2,000 requests/minute (per user)
  5. Self-hosted: Configurable by admin

  6. Implement delays for large deployments:

    # Deploy to many projects with delays
    # Space out secret syncs
    

  7. Deploy to group level instead of many projects:

    # Good: Single group variable
    config:
      group: myorg
    
    # Avoid: Many individual project variables
    # (multiple API calls)
    

  8. Use dry-run to test without API calls:

    secretzero sync --dry-run -f Secretfile.yml
    

Self-Hosted GitLab Support

Configuration

providers:
  gitlab_selfhosted:
    kind: gitlab
    auth:
      kind: token
      config:
        token: ${GITLAB_SELFHOSTED_TOKEN}
        url: https://gitlab.mycompany.com

secrets:
  - name: api_key
    kind: random_string
    config:
      length: 32
    targets:
      - provider: gitlab_selfhosted
        kind: gitlab_variable
        config:
          project: myorg/myproject
          masked: true

URL Format

  • GitLab.com: https://gitlab.com (default, can be omitted)
  • Self-hosted GitLab: https://gitlab.mycompany.com
  • Custom port: https://gitlab.mycompany.com:8443

Note: Do not include /api/v4 in the URL - the provider handles API path construction.

Testing Self-Hosted Connection

# Test API connectivity
curl --header "PRIVATE-TOKEN: $GITLAB_SELFHOSTED_TOKEN" \
  "https://gitlab.mycompany.com/api/v4/user"

# Test with SecretZero
secretzero test -f Secretfile.yml

Self-Hosted Specific Considerations

  1. SSL Certificates:
  2. Ensure valid SSL certificate
  3. For self-signed certificates, configure trust in system

  4. Network Access:

  5. Verify network connectivity to GitLab server
  6. Check firewall rules and proxy settings

  7. API Version:

  8. Uses /api/v4 endpoint (GitLab 9.0+)
  9. Verify GitLab version compatibility

  10. Rate Limits:

  11. Self-hosted instances may have custom rate limits
  12. Contact GitLab admin for specific limits

  13. Authentication:

  14. LDAP/SAML users may have restrictions on API access
  15. Verify API access is enabled for your account

Cost and Limits

GitLab CI/CD Usage

  • Public projects: Unlimited CI/CD minutes on GitLab.com
  • Private projects: Free tier includes minutes (varies by plan)
  • Variables: No additional cost, included in GitLab plan

Limits

  • Project variables: Up to 200 variables per project
  • Group variables: Up to 200 variables per group
  • Variable size: Maximum 10 KB per variable value
  • Variable name: Maximum 255 characters

Best Practices for Limits

  1. Use group variables for shared secrets to reduce per-project variable count
  2. Use file variables for large data instead of environment variables
  3. Implement variable cleanup for unused secrets
  4. Plan naming conventions to stay organized within limits

See Also