GitLab Basics for Deployment Pipelines: Projects, Groups, Roles, Protected Branches
AI generated
CI/CD
.yml
GitLab · CI/CD · Magento · Repository Governance
GitLab Basics for Deployment Pipelines
Projects, Groups, Roles, Protected Branches

Without clear repository governance, even well-written pipelines are organizationally fragile. Projects, groups, access roles, and protected branches form the foundation on which secure Magento deployment pipelines can be built.

15 min read Projects · Groups · Roles · Protected Branches · CI/CD Variables GitLab 16+ · Magento 2.4 · PHP 8.4

1. Why Repository Governance Comes Before the Pipeline

A GitLab CI/CD pipeline is only as reliable as the organizational rules that surround it. Anyone who starts directly with the .gitlab-ci.yml file, without first clarifying who is allowed to push to which branch, which variables apply in which environment, and how approvals for production are controlled, is building on unstable ground. The GitLab basics for deployment pipelines therefore don't start with syntax but with structural decisions: projects, groups, roles, and branch protection are the foundation on which reproducible deployments are built.

This point is especially relevant for Magento projects. A Magento store consists of several layers: custom modules, theme, Composer dependencies, configuration files, and environment variables, all of which need to be managed correctly in the pipeline. If every developer can push directly to main and production variables are visible to everyone, a stable deployment process is barely achievable. The governance decisions made at the start of a project determine how much manual oversight is still needed later on.

The following sections cover the most important GitLab core concepts in the context of recurring Magento deployments: starting with project structure, moving through groups and roles, and ending with protected branches and CI/CD variables with environment scope. Anyone who understands these building blocks and applies them deliberately creates the conditions for even complex deployment pipelines to stay maintainable and secure in the long run.

2. Structuring GitLab Projects Correctly

A GitLab project is more than a Git repository. It's the container for code, pipeline configuration, CI/CD variables, environments, deploy keys, and access rules. For Magento projects it's a good idea to manage the entire store code, meaning custom modules, theme, Composer dependencies, and configuration files, in a single monorepo. That simplifies the pipeline logic considerably: one build trigger, one artifact, one deploy job. Separate repositories for theme and modules increase complexity and require extra coordination between pipelines.

The project settings in GitLab under Settings > General contain important deployment-relevant options: Visibility should be set to Private for production repositories. Merge Requests should be enabled so that no code can reach protected branches without review. The Delete source branch after merge option keeps the branch tree tidy. The default branch is set under Settings > Repository, for Magento projects main is the standard.

# .gitlab-ci.yml: Project-level pipeline configuration
# Default branch: main | Protected branches: main, release/*
# All secrets managed via Settings > CI/CD > Variables with environment scope

default:
  tags:
    - magento-runner       # Restrict jobs to dedicated runner
  retry:
    max: 1
    when: runner_system_failure

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'

3. Groups and Subgroups for Magento Teams

GitLab groups make it possible to manage multiple projects under a shared namespace level and define access rights, runners, and CI/CD variables at the group level. For agencies or teams that maintain several Magento stores, a sensible group structure is essential. A typical structure might have mironsoft/ as the main group with subgroups per client, for example mironsoft/client-a/shop and mironsoft/client-b/shop. Variables defined at the group level automatically apply to all projects in the group, which considerably simplifies managing shared secrets like COMPOSER_AUTH.

Group runners registered at the group level are available to all projects in the group. This is especially useful when several Magento stores need to share the same build runner without each project having to configure its own. Subgroups allow fine-grained separation here: a runner for test environments can be restricted at the subgroup level without touching the production runners.

4. Understanding Access Roles and Permissions

GitLab has five roles: Guest, Reporter, Developer, Maintainer, and Owner. For recurring deployments, the differences between Developer and Maintainer matter most. Developers can push branches, create merge requests, and run pipelines, but they cannot push directly to protected branches or manage CI/CD variables. Maintainers can additionally configure protected branches and tags, register runners, and change project settings. In a Magento context, developers should generally be assigned the Developer role, while the DevOps lead is given Maintainer rights.

Deploy keys are another important permission layer: they allow read-only or read-write access to a repository for external systems, without needing a personal access token with full user rights. For deployment scripts that access the server, a separate deploy key with read-only access is the safe choice. Combined with SSH keys for the deployment user on the target server, this creates an access model without unnecessarily broad permissions.

# Role-based pipeline restrictions
# Only Maintainers can trigger production deployments via protected tags

deploy:production:
  stage: deploy
  environment:
    name: production
    url: https://shop.mironsoft.de
  rules:
    # Trigger only on protected version tags (pushed by Maintainer)
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
      when: manual
  script:
    - echo "Deploying release $CI_COMMIT_TAG to production"
    - bash deploy/deploy.sh
  allow_failure: false

5. Protected Branches as a Deployment Gate

Protected branches are the central governance tool in GitLab. They define who can push directly to a branch, who can merge merge requests, and whether force pushes are allowed. For Magento deployment pipelines the configuration is clear: main is set up as a protected branch with Allowed to push: No one and Allowed to merge: Maintainers. That means no developer can push directly to main, all changes have to go through a merge request with review.

In addition to main, release branches such as release/* should also be configured as protected branches. These branches are used for hotfix workflows and release candidates and likewise may only be merged by maintainers. The Code owner approval option, available in GitLab Premium, makes it possible to enforce a mandatory review by the responsible code owner for specific directories such as app/etc/ or .gitlab-ci.yml. This is particularly useful for security-critical configuration files.

6. Protected Tags for Release Approval

While protected branches secure the workflow for ongoing development, protected tags control the moment of release approval. The pattern for Magento deployments: a production deploy is only triggered when a tag matching the pattern v* is created, and tags may only be created by maintainers. That creates a natural approval gate that prevents code from unintentionally reaching production.

Protected tags are configured under Settings > Repository > Protected tags. The pattern v* protects all tags that start with v, so v1.2.3, v2.0.0-rc1, and so on. Only roles with Maintainer permission or higher can create such tags. In the .gitlab-ci.yml a deploy job can then be configured exclusively for these tags via the rule if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'. This connects repository governance directly with the pipeline logic.

# Protected tag pattern in .gitlab-ci.yml
# Only runs when a Maintainer creates a semantic version tag

stages:
  - build
  - test
  - package
  - deploy
  - verify
  - rollback

variables:
  GIT_STRATEGY: fetch
  COMPOSER_CACHE_DIR: .cache/composer
  NPM_CONFIG_CACHE: .cache/npm
  RELEASE_RETENTION: "5"

# Shared build configuration, reused across deploy jobs
.deploy_base:
  image: alpine:3.19
  before_script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts

7. CI/CD Variables with Environment Scope

CI/CD variables in GitLab are the configuration contract of the pipeline. They can be defined at the project, group, or instance level, and given an environment scope that determines which environment a variable is active in. For Magento deployments this means: DEPLOY_HOST for the staging environment has a different value than DEPLOY_HOST for production, both variables share the same name but are separated through the environment scope. GitLab automatically picks the correct variable based on the environment name of the current job.

Sensitive variables such as SSH_PRIVATE_KEY, COMPOSER_AUTH, and database passwords should be marked as Masked so they don't appear in plain text in job logs. The Protected option ensures a variable is only available in jobs running on protected branches or protected tags, a critical security mechanism that prevents production secrets from becoming visible in feature branch pipelines. This combination of masking and the protected flag is mandatory for all Magento production secrets.

# Environment-scoped variable usage in deploy jobs
# Variables SSH_PRIVATE_KEY, DEPLOY_HOST, DEPLOY_PATH are scoped per environment

deploy:staging:
  extends: .deploy_base
  stage: deploy
  environment:
    name: staging
    url: https://staging.mironsoft.de
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: on_success
  script:
    - |
      RELEASE_ID="$(date +%Y%m%d-%H%M%S)"
      RELEASE_PATH="${DEPLOY_PATH}/releases/${RELEASE_ID}"
      ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p ${RELEASE_PATH}"
      rsync -az --delete --exclude='.git' ./ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"
      ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "
        ln -sfn ${DEPLOY_PATH}/shared/app/etc/env.php ${RELEASE_PATH}/app/etc/env.php
        ln -sfn ${RELEASE_PATH} ${DEPLOY_PATH}/current
        cd ${DEPLOY_PATH}/current && bin/magento cache:flush
      "
      echo "RELEASE_ID=${RELEASE_ID}" >> deploy.env
  artifacts:
    reports:
      dotenv: deploy.env

8. Governance Patterns Compared

How much repository governance a project needs depends on team size, risk profile, and the number of environments. A simpler structure can be enough for a single-developer project, while team projects with multiple developers and a production environment benefit from full governance.

Governance Aspect Minimal Configuration Recommended Configuration Benefit
Default Branch master (GitLab default) main, explicitly set Consistency with CI/CD rules
Branch Protection No protection main + release/* protected No direct push to critical branches
Tag Protection All tags allowed v* for maintainers only Controlled release approval
CI/CD Variables Global, no scopes Protected + masked + scoped Secrets only in the right environment
Merge Requests Optional, no review Required with approval rule Four-eyes principle before merge

In practice it turns out that teams who only introduce governance rules after the first production incident pay a higher price than teams who establish clear structures from the start. Setting up protected branches, tag protection, and environment-scoped variables takes about an hour, and experience shows it saves many hours of incident management and manual rework.

9. Summary

The GitLab basics for deployment pipelines, projects, groups, roles, and protected branches, are not bureaucratic overhead but the structural foundation for every further pipeline decision. Anyone who configures these building blocks deliberately creates a framework in which developers can work safely without accidentally touching critical branches or production environments. Protected branches prevent direct pushes to main. Protected tags control who is allowed to trigger releases. Environment-scoped variables cleanly separate staging and production secrets.

This structure is especially important for Magento projects because a faulty deploy can directly cause revenue loss. The effort for the initial configuration is manageable and the decisions involved aren't hard, but they need to be made deliberately and completely. A deployment process that ignores these basics might work during the test phase, but it quickly breaks down under real conditions with multiple developers and genuine release cycles.

GitLab Basics: The Essentials at a Glance

Repository Governance

Protected branches for main and release/*, no direct push, all changes go through merge requests with review.

Access Roles

Developers as Developer, DevOps leads as Maintainer. Only maintainers may create protected tags and trigger deployments.

CI/CD Variables

All secrets as protected, masked, and environment-scoped. Staging and production get their own values for the same variable name.

Release Approval

Production deploys only through protected tags with the pattern v*. No automatic deployment without explicit approval from a maintainer.

10. Checklist: Project Setup for the First Pipeline

Before the first pipeline for a Magento project is activated, all governance settings should be configured correctly. The following checklist summarizes the most important points and makes sure the project setup meets the requirements for secure, recurring deployments.

# Project setup checklist: verify before first pipeline run
# Settings > General
# - Visibility: Private
# - Merge Requests: Enabled
# - Squash commits: Optional
# - Delete branch after merge: Enabled

# Settings > Repository > Protected Branches
# - main: Allowed to push = No one, Allowed to merge = Maintainers
# - release/*: Allowed to push = No one, Allowed to merge = Maintainers

# Settings > Repository > Protected Tags
# - v*: Allowed to create = Maintainers

# Settings > CI/CD > Variables
# - SSH_PRIVATE_KEY: Protected, Masked, Scope = production/*
# - SSH_KNOWN_HOSTS: Protected, Scope = production/*
# - DEPLOY_HOST: Protected, Scope = production (different value per env)
# - DEPLOY_USER: Protected, Scope = *
# - DEPLOY_PATH: Protected, Scope = * (different value per env)
# - COMPOSER_AUTH: Protected, Masked, Scope = *
# - RELEASE_RETENTION: Not protected, Scope = *, Value = 5

This checklist is the starting point for every recurring deployment. Anyone who goes through it carefully once and configures every point correctly creates a solid foundation on which all further pipeline jobs, build, test, deploy, verify, and rollback, can be built safely and reproducibly. The technical pipeline is only as good as the organizational rules that surround it.

11. FAQ: GitLab Basics for Deployment Pipelines

1Difference between project and group?
Project = single codebase with pipeline and variables. Group = namespace for multiple projects with shared runners, variables, and access rights.
2Why main as a protected branch?
Prevents direct pushes. All changes go through merge requests with review, protecting against unintended deployments and broken code states on the main line.
3How do environment-scoped variables work?
Same variable name, different values per environment. GitLab automatically picks the matching value based on the job's environment name.
4Masked vs. protected variables?
Masked: not visible in job logs. Protected: only available on protected branches/tags. Combine both options for production secrets.
5When do I need my own runner?
For SSH access to your own servers, specific build tools, or security requirements. Shared runners are not enough for production deployments to your own infrastructure.
6Separating staging secrets from production?
Environment scoping plus the protected flag. Staging variable with scope staging, production variable with scope production, GitLab separates them automatically.
7What are deploy keys?
SSH keys for read-only or read-write access to a specific repository. Safer than personal access tokens for external systems and CI runners.
8Protected tags for release control?
Yes. The pattern v* protects release tags, only maintainers can create them. Pipeline jobs with an if rule on the tag pattern then only run for genuine releases.
9Monorepo or multiple repositories?
For Magento, almost always a monorepo: one build, one artifact, one pipeline. Multiple repositories increase complexity without a proportional benefit.
10What happens without branch protection?
Any developer can push directly to main, there's no technical barrier against faulty production deployments. Governance needs to be configured from the start.