GitLab CI’s branch protection and secret scoping are fundamental to preventing accidental deployments to sensitive environments and ensuring that secrets are only available where they’re intended.

Let’s see this in action. Imagine you have a production branch that should never be directly pushed to. Developers should merge into main, and a CI job then merges main to production.

Here’s a .gitlab-ci.yml that uses branch protection and secret scoping:

stages:
  - build
  - deploy

variables:
  PRODUCTION_DB_PASSWORD: $PRODUCTION_DB_PASSWORD # This secret is only available in specific environments

build_app:
  stage: build
  script:
    - echo "Building the application..."
    - echo "APP_VERSION=$CI_COMMIT_SHA" > build.env
  artifacts:
    reports:
      dotenv: build.env

deploy_to_staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
    - echo "Using DB password: $STAGING_DB_PASSWORD" # Assumed to be set in GitLab CI/CD settings for staging
  environment: staging
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

deploy_to_production:
  stage: deploy
  script:
    - echo "Deploying to production..."
    - echo "Using DB password: $PRODUCTION_DB_PASSWORD"
    - echo "Merging main to production..."
    - git checkout production
    - git merge $CI_COMMIT_SHA
    - git push origin production
  environment: production
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual # Require manual approval for production deployments

Now, let’s talk about how GitLab enforces this.

Branch Protection: The Gatekeepers

Branch protection is your first line of defense. It dictates who can push to, merge into, or delete specific branches. This is configured in your project’s Settings > Repository > Protected branches.

  • Preventing direct pushes: You can configure a branch (e.g., production) to disallow all pushes. This means no developer can git push origin production. They must go through a merge request and CI/CD pipeline.
  • Requiring MRs and approvals: For critical branches like main or production, you can enforce that changes can only be merged via a Merge Request. You can also specify a minimum number of approvals required before a merge is allowed. This adds a human review step.
  • Role-based access: You can grant push/merge permissions to specific roles (e.g., Maintainers) or even individual users. This ensures only trusted individuals can make direct changes to sensitive branches.

In our example, if you try to git push origin production directly, GitLab will reject it with a permission error, forcing you to use the established merge request workflow.

Scoping Secrets: The Lockboxes

Secrets (API keys, database passwords, certificates) are sensitive and should not be exposed unnecessarily. GitLab CI/CD allows you to scope these secrets so they are only available to specific jobs, environments, or branches. This is managed in Settings > CI/CD > Variables.

  • Environment Scoping: This is the most powerful way to scope secrets. You can define a variable (e.g., PRODUCTION_DB_PASSWORD) and associate it with the production environment. This variable will only be available to jobs running in the production environment. In our example, $PRODUCTION_DB_PASSWORD is only injected into the deploy_to_production job because that job is explicitly assigned to the production environment.
  • Protected Variables: If a variable is marked as "Protected," it’s only available to jobs running on protected branches. This is a good practice for secrets that should never leak, even in development branches.
  • Masked Variables: While not scoping in the same sense, masking prevents secrets from appearing in job logs. This is a crucial complementary security measure.

The deploy_to_staging job in our example assumes a $STAGING_DB_PASSWORD is available. If this job were not assigned to the staging environment, and $PRODUCTION_DB_PASSWORD was the only password variable defined, the deploy_to_staging job would fail because $PRODUCTION_DB_PASSWORD wouldn’t be injected.

The most surprising true thing about GitLab’s secret scoping is that environment variables defined directly in the .gitlab-ci.yml file (like APP_VERSION in our build job) are not subject to environment scoping rules for secrets. They are available to any job that runs. Only variables defined in the GitLab UI under Settings > CI/CD > Variables can be environment-scoped.

By combining branch protection with carefully scoped secrets, you create a robust pipeline that minimizes the risk of accidental exposure or deployment to critical environments.

The next concept you’ll likely encounter is managing different deployment strategies like canary releases or blue-green deployments, which build upon this foundation of controlled deployments.

Want structured learning?

Take the full Gitlab-ci course →