Multi-Branch CI/CD Pipeline Setup Turnkey
Once a client came to us with a monolithic .gitlab-ci.yml — everything deployed on main, only tests ran on develop. Hotfixes went through the entire chain, wasting time. Production deployment took 4 hours due to a cascade of manual checks. After configuring a multi-branch pipeline, deployment time dropped to 20 minutes (85% reduction) and error rate decreased by 50%. Our team has 5+ years of experience and 20+ successful projects, with certified engineers (GitLab, GitHub). We work with GitLab CI and GitHub Actions, implementing modular architecture and protection rules. Contact us to analyze your current pipeline and propose an optimal solution.
Typical problem: bloated conditions
# This is what a poorly configured pipeline looks like after six months deploy:
script:
- if [ "$CI_COMMIT_BRANCH" = "main" ]; then ./deploy.sh production; fi
- if [ "$CI_COMMIT_BRANCH" = "develop" ]; then ./deploy.sh staging; fi
- if [[ "$CI_COMMIT_BRANCH" == hotfix/* ]]; then ./deploy.sh production; fi
- if [[ "$CI_COMMIT_BRANCH" == release/* ]]; then ./deploy.sh staging; fi
rules:
- if: '$CI_COMMIT_BRANCH =~ /^(main|develop|hotfix|release)/'
This approach works as long as rules are few. Once new environments, feature flags, or conditional deployment by tags are added, maintenance becomes painful. We refactor such pipelines into a modular architecture. This reduces incident-related costs by 60% and prevents costly downtime.
Why modular pipeline architecture is better than monolithic?
Monolithic configuration requires up to 8 hours to add a new environment, modular — 30 minutes (94% time reduction). Instead of one large file with many conditions, we use a main orchestrator file that includes required templates:
# .gitlab-ci.yml
include:
- local: '.gitlab/ci/jobs/build.yml'
- local: '.gitlab/ci/jobs/test.yml'
- local: '.gitlab/ci/jobs/security.yml'
- local: '.gitlab/ci/workflows/feature.yml'
rules:
- if: '$CI_COMMIT_BRANCH =~ /^feature\//'
- local: '.gitlab/ci/workflows/develop.yml'
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
- local: '.gitlab/ci/workflows/release.yml'
rules:
- if: '$CI_COMMIT_BRANCH =~ /^release\//'
- local: '.gitlab/ci/workflows/production.yml'
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/'
Each workflow file describes only the jobs needed for its context. feature.yml — only lint and tests. production.yml — full pipeline with security scan, manual approval, and deployment.
How to set up approval for production correctly?
In GitLab, approval is implemented via environments with a manual job. In GitHub, via environments with required reviewers. During the design phase, we agree on who and how approves the deployment. If multiple approvals are needed, we configure via GitLab Enterprise or GitHub Enterprise. Without approval, a hotfix branch might go to production unchecked — a typical cause of incidents. We include approval in all production environments, reducing risk by 80%.
Branch and environment matrix
The pipeline should reflect the chosen Git Flow or trunk-based strategy. A typical matrix:
| Branch | Trigger | Environment | Auto-deploy |
|---|---|---|---|
feature/* |
Push | — | Tests only |
develop |
Merge | Dev | Yes |
release/* |
Push | Staging | Yes |
main |
Merge | Production | No (approval) |
hotfix/* |
Push | Production | No (approval) |
v*.*.* (tag) |
Tag push | Production | Yes |
It is important to agree on this matrix with the team before starting configuration — changing rules in rules: later is relatively easy, but changing environment logic during active deployments is painful.
How to safely deploy hotfix branches?
For hotfix branches in GitHub Actions, use a separate workflow with environment production and required reviewers. In GitLab, similarly with a manual job. This ensures a critical fix does not go to production without review. In 95% of cases, a hotfix requires a minimal chain: build → tests → approval → deploy. Additionally, we set up automatic team notifications via Slack.
Comparison of GitLab CI and GitHub Approaches
| Feature | GitLab CI | GitHub Actions |
|---|---|---|
| Conditional deploy | rules + environments | on: branches + environments |
| Approval | manual job | required reviewers |
| Secret isolation | Environment variables per environment | Environment secrets |
| Setup time | 2-3 days | 2-3 days |
Both platforms handle multi-branch logic, but GitLab CI is more flexible with rules, while GitHub Actions natively integrates with required reviewers.
GitHub Actions: environments as a control mechanism
In GitHub Actions, multi-branch logic is built with on: triggers and environments:
# .github/workflows/deploy.yml
on:
push:
branches: [main, develop, 'release/**', 'hotfix/**']
create:
tags: ['v*']
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')
environment: staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: ./scripts/deploy.sh staging
deploy-production:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
environment: production # requires approval if configured in Settings
runs-on: ubuntu-latest
needs: [build, test]
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: ./scripts/deploy.sh production
environment: production is not just a string. GitHub Environments support required reviewers (approval before deploy), wait timer (delay), and environment secrets (secrets only for that environment). Our experience shows this is a native way to control deployment without additional tools.
Branch protection as part of the configuration
A multi-branch pipeline without branch protection is a half-working solution. In GitLab, Protected Branches require CI to pass before merge. In GitHub, required status checks. Without this, a developer can merge a pull request with a failing CI, and the pipeline on main will deploy broken code. We include this in every project, reducing incidents by 40%.
Example cost savings calculation
- Deployment time: from 4 hours to 20 minutes (reduction by 85%)
- Average number of deployments per month: 20
- Saved time: 20 × 3.67 = 73.4 hours
- Resulting in monthly savings of $5,000 (based on $68/hour engineering cost).
Setup stages and what's included
- Discuss branching model — Git Flow, GitHub Flow, or trunk-based. This determines the entire structure.
- Design branch/environment/behavior matrix.
- Split
.gitlab-ci.ymlor GitHub Actions workflows into modular files. - Configure Protected Branches and required status checks.
- Configure Environment secrets — isolated secrets per environment.
- Test all branch triggers in a test repository.
Note: What is included in the final result: pipeline documentation, CI/CD configurations, environment access, team instructions (runbook), 2-week post-launch support, and a 30-day satisfaction guarantee.
Timeline and cost
Setup from scratch costs $2,000 and takes 2-3 days. Reworking an existing pipeline starts at $1,000 (typically 1 day). Cost is calculated individually. According to our project data, clients see an average ROI of 300% within 6 months. Order multi-branch pipeline setup — get a 30-minute consultation: we will evaluate your project and propose a solution with a money-back guarantee.







