Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXCII · Protected EnvironmentsBranches

Deployment branch restriction — only the right source can deploy to production

Advanced⏱ ~23 mingit

What you'll learn

  • Configure a deployment branch restriction limiting deploys to specific branches or tag patterns
  • Recognise the branch restriction as the source-of-truth gate independent of human reviewers
  • Combine the branch restriction with a tag trigger to build the canonical production deploy shape
  • Audit the branch list quarterly to catch drift

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The deployment branch restriction is the third canonical protection rule on a protected environment. Required reviewers and the wait timer gate the deploy on a human side; the branch restriction gates the deploy on a source side. The two roles are independent and complementary. This lesson covers the branch restriction in detail and shows how it composes with a tag trigger to produce the canonical production deploy shape.

The branch restriction as a source-of-truth gate

The branch restriction is a list of branch patterns and tag patterns that are permitted to deploy to the environment. A workflow whose trigger branch does not match any pattern cannot write to the environment, regardless of how many reviewers approve. The rule is evaluated by the platform before the deploy job runs; the platform refuses to even hold the job for review.

flowchart TD
    A["Workflow triggers"] --> B{"Branch matches pattern?"}
    B -- "no" --> X["Job rejected by platform"]
    B -- "yes" --> C["Job held for reviewers"]
    C --> D["Reviewers approve"]
    D --> E["Wait timer elapses"]
    E --> F["Apply with environment identity"]

The order matters: the branch restriction is the first gate. A workflow from feature/foo that declares environment: production is rejected by the platform before any reviewer is notified. This is the operational reason the rule matters: a configuration with two of three canonical rules has a gate that the wrong source can walk through.

Configuring the branch restriction

The real command on GitHub uses the --deployment-branch-policy flag with branch patterns:

gh environment edit production \
  --deployment-branch-policy \
  --branch-patterns main,release/*

The patterns support * as a wildcard. The list shown permits deploys from main and from any branch matching release/*. A tag pattern uses --tag-patterns instead:

gh environment edit production \
  --deployment-branch-policy \
  --tag-patterns 'v*.*.*'

The tag pattern permits deploys triggered by tags matching the semver-style pattern v*.*.* (e.g., v1.2.3). A production environment typically uses both a branch pattern for hotfix deploys and a tag pattern for release deploys.

gh environment view production

The view subcommand reports the configured patterns. The audit must confirm both the branch and tag lists are populated; an empty list is a restriction that permits nothing, which is also a configuration completed but not finished - in the opposite direction.

The canonical production deploy shape

The canonical production deploy combines four guards: a tag trigger, an environment binding, a reviewer pool, and a branch restriction. The workflow file declares the first two; the platform configuration declares the second two.

on:
  push:
    tags: ['v*.*.*']
jobs:
  deploy-prod:
    environment:
      name: production
    runs-on: ubuntu-latest
    steps:
      - run: deploy.sh production

The trigger is a tag matching v*.*.*. The job targets the production environment. The platform evaluates the environment’s rules: branch restriction (matches the tag pattern), reviewer pool (held for approval), wait timer (elapses), then apply. Four guards; all must agree.

A team that deploys from main rather than from tags uses a push trigger instead:

on:
  push:
    branches: ['main']

The shape is the same: a trigger that limits the source, an environment binding, and the platform’s rules. The discipline is to combine the trigger filter with the branch restriction so neither is the only guard.

What the branch restriction does not catch

The branch restriction catches the wrong source: a feature-branch workflow, a fork PR, an unmerged branch. It does not catch the wrong content on a permitted branch: a commit on main that introduces a bad change is still on main, still matches the pattern, and still deploys. The reviewer pool and the wait timer are the rules that catch the wrong content. The three rules are independent and complementary; none replaces another.

Production discipline

  1. Configure a non-empty branch restriction. An empty list permits nothing; a list with one entry is a single point of failure.
  2. Combine the branch restriction with a trigger filter. Neither is the only guard.
  3. Audit the branch list quarterly. Old release branches accumulate; unused branches are leaked branches.
  4. Treat the restriction as a source-of-truth gate. A deploy from the wrong branch is rejected before any human involvement.

Cross-course references

  • This course, Part IX-04 (Branch strategies and protection) covers the branch model the restriction assumes.
  • This course, Part LVII-03 (Protected environments and required reviewers) introduces the three rules.
  • Terraform for Production Sysadmins - Part XXVIII covers the canonical promotion pattern.

Quiz

Knowledge check · 4 questions

  1. Q1. A team has a production environment with required reviewers and a wait timer but no branch restriction. An engineer opens a PR from `feature/new-iac-pattern`; the workflow declares `environment: production`; reviewers approve; the wait timer elapses; the feature branch's code reaches production. What rule was missing?

  2. Q2. The branch restriction catches the wrong content on a permitted branch: a commit on `main` that introduces a bad change is still on `main` and still matches the pattern.

  3. Q3. Name the canonical production deploy shape: the four guards that combine into a release.

  4. Q4. Diagnose why a feature-branch deploy reached production despite the team believing the branch restriction was configured.

    A team configures the production environment with required reviewers, a wait timer, and a branch restriction listing `main` and `release/*`. Six months later, the team adds a new branch called `hotfix-dec22` for an urgent fix. The engineer configuring the new branch inadvertently clears the existing branch list and adds only `hotfix-dec22`. The next deploy from `main` is rejected by the platform because `main` is no longer in the list. The team debugs the rejection, sees `hotfix-dec22` is permitted, and assumes the original list was over-restrictive. They leave the configuration as `hotfix-dec22` only. Two weeks later, a feature branch workflow opens a PR, gets merged to a similarly-named branch, and deploys to production with no source-of-truth check.

Passing score: 75%. Answers are checked in this browser.