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.
Table of Contents
- 1. Why Repository Governance Comes Before the Pipeline
- 2. Structuring GitLab Projects Correctly
- 3. Groups and Subgroups for Magento Teams
- 4. Understanding Access Roles and Permissions
- 5. Protected Branches as a Deployment Gate
- 6. Protected Tags for Release Approval
- 7. CI/CD Variables with Environment Scope
- 8. Governance Patterns Compared
- 9. Summary
- 10. Checklist: Project Setup for the First Pipeline
- 11. FAQ
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.