Multiple Magento Projects
Running ten Magento shops with ten identical pipeline files is technical debt in its purest form. Every change to the deploy process has to be made ten times. GitLab templates with include and extends solve this problem: one central build standard, individually extensible per project.
Table of Contents
- 1. The problem with copied pipeline files
- 2. GitLab include: basics and variants
- 3. extends: deriving and overriding jobs from templates
- 4. YAML anchors as an alternative within a single file
- 5. A central template repository for Magento projects
- 6. Project-specific overrides and extensions
- 7. Template versioning and breaking changes
- 8. Comparison: copied pipelines vs. template-based pipelines
- 9. Pitfalls of template reuse
- 10. Summary
- 11. FAQ
1. The problem with copied pipeline files
Agencies and teams running multiple Magento projects know this problem well. The first .gitlab-ci.yml is carefully built, tested and confirmed to work. For the second project it gets copied and slightly adjusted. For the third project, the copy of the second project's file gets copied again. After a year, ten pipeline files exist, each differing in details because improvements only made it into some of the projects. A security hole in the deploy script now has to be fixed in ten repositories individually.
This is not an academic problem, it is an everyday operational reality in agencies with multiple client projects. The solution is the same one software developers have used for decades to fight code duplication: abstraction and reuse. GitLab offers the include keyword for pulling external YAML files into the current pipeline, and extends for deriving and extending job definitions from included templates.
The goal is a system in which the shared build, test and deploy standard is defined in exactly one place, the template repository, and every project only includes it and configures what is actually project-specific. An improvement to the deploy standard is made once and instantly becomes available to every project that references the template at a compatible version.
2. GitLab include: basics and variants
The include keyword in GitLab CI/CD lets you pull external YAML files into the current pipeline configuration. There are four variants: local (a file in the same repository), project (a file in another GitLab project), remote (a URL pointing to an external YAML file) and template (predefined GitLab templates). For reuse across multiple Magento projects, the project variant is the most suitable, because it references a central template repository on the same GitLab instance and benefits from GitLab's access control.
An important detail of include: project is the ref option, which determines which branch, tag or commit SHA from the template repository is used. When ref points to a fixed tag, the template version is frozen and changes to the template repository do not immediately affect every project. When ref points to main, every project always uses the newest template version, convenient for small teams, but risky because breaking changes hit all projects immediately.
# .gitlab-ci.yml in a Magento project
# Includes templates from a central template repository
include:
# Pin to a specific version tag for stability
- project: "mironsoft/gitlab-ci-templates"
ref: "v2.1.0"
file:
- "/magento/build.yml"
- "/magento/test.yml"
- "/magento/deploy.yml"
# Project-specific variables override template defaults
variables:
MAGENTO_VERSION: "2.4.8"
PHP_VERSION: "8.4"
DEPLOY_HOST: "shop1.example.com"
DEPLOY_PATH: "/var/www/shop1"
THEME_PATH: "app/design/frontend/Mironsoft/default"
# Project-specific stage additions (template stages remain)
stages:
- build
- test
- package
- deploy
- verify
- rollback
3. extends: deriving and overriding jobs from templates
The extends keyword lets you derive a job from a template and override or extend individual properties. GitLab performs a deep merge under the hood: dictionaries (such as variables and cache) are merged, scalars and lists are overwritten. This enables fine-grained overrides without having to copy the entire job definition.
A typical pattern is to define a template job describing the standard build process for Magento, and in each project override only the project-specific deviations. The template defines the PHP image, the composer options, the cache and the artifact configuration. The project overrides only the specific theme directory or adds project-specific build steps.
# templates/magento/build.yml (in template repository)
# Base build job for Magento projects, extended in each project
.build:magento:base:
stage: build
image: "php:${PHP_VERSION:-8.4}-cli"
variables:
COMPOSER_CACHE_DIR: "${CI_PROJECT_DIR}/.cache/composer"
COMPOSER_HOME: "${CI_PROJECT_DIR}/.cache/composer"
THEME_PATH: "app/design/frontend/Mironsoft/default" # Default theme
cache:
key:
files:
- composer.lock
paths:
- .cache/composer/
policy: pull-push
script:
- composer install --no-dev --prefer-dist --optimize-autoloader
- npm ci --prefix "${THEME_PATH}/web/tailwind"
- npm run build --prefix "${THEME_PATH}/web/tailwind"
- bin/magento setup:di:compile
artifacts:
paths:
- vendor/
- generated/
- "${THEME_PATH}/web/tailwind/css/"
expire_in: 3 hours
---
# .gitlab-ci.yml in a specific project, extends the template
build:magento:
extends: .build:magento:base
variables:
THEME_PATH: "app/design/frontend/Acme/custom" # Project-specific override
script:
- !reference [.build:magento:base, script] # Include base script
- echo "Project-specific post-build step"
4. YAML anchors as an alternative within a single file
YAML anchors are a YAML-native solution for reuse within a single file. With &anchor-name a block is defined as an anchor, with *anchor-name it is referenced, and with <<: *anchor-name its values are merged into another block. GitLab CI/CD fully supports YAML anchors, which makes them useful for reuse within a project's local .gitlab-ci.yml.
However, YAML anchors are not a solution for cross-project reuse, because they cannot be imported from external files. They are well suited to avoiding duplication within a single file, not as a substitute for the include system. In practice, YAML anchors are often used for common variable blocks and SSH setup steps that are needed across multiple jobs in the same pipeline.
5. A central template repository for Magento projects
The central template repository is the heart of template reuse. It holds the shared building blocks for every Magento project: build jobs, test jobs, deploy jobs, verify jobs and rollback jobs. The structure should be organized by functional area so that projects only include the parts they actually need. A project that does not need a rollback job simply does not include rollback.yml.
The template repository needs its own pipeline that validates the templates. GitLab provides the ci-lint API endpoint for this, which checks a pipeline configuration syntactically and semantically. For the template repository this means a test job in the template pipeline runs a lint check across all template files and makes sure no syntactically invalid configuration is ever published.
# Structure of the central template repository
# mironsoft/gitlab-ci-templates/
# magento/build.yml : composer, npm, di:compile, static content
# magento/test.yml : PHPStan, PHPCS, PHPUnit
# magento/deploy.yml : SSH, rsync, symlink switch, shared files
# magento/verify.yml : health check, smoke tests, cache status
# magento/rollback.yml : symlink switch to previous release
# magento/cleanup.yml : remove old releases, clean old artifacts
# Template repository pipeline (validates all templates)
# .gitlab-ci.yml (in the template repo itself)
validate:templates:
stage: test
image: alpine:latest
script:
- apk add --no-cache curl
# Validate each template file against the GitLab CI lint API
- |
for file in magento/*.yml; do
echo "Validating: $file"
curl -sf \
--header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
--header "Content-Type: application/json" \
--data "{\"content\": \"$(cat ${file} | jq -Rs .)\"}" \
"${CI_API_V4_URL}/ci/lint" | jq -e '.valid == true'
done
6. Project-specific overrides and extensions
Not every Magento project is identical. Some projects have additional stores with different themes, others deploy to multiple server environments, and still others have specific testing requirements. The template system must allow overrides and extensions without compromising the reusability of the base templates.
The recommended strategy is to pass project-specific configuration through variables that are defined as defaults in the template. If the template sets MAGENTO_LOCALE: "de_DE" as a default and a project is multilingual, the project overrides the variable with MAGENTO_LOCALE: "de_DE en_US fr_FR". The template job consumes the variable without the job code ever changing. Only when a project's requirement fundamentally goes beyond what the template offers is the job derived with extends and the differing steps overridden.
# Project-specific .gitlab-ci.yml, minimal override pattern
include:
- project: "mironsoft/gitlab-ci-templates"
ref: "v2.1.0"
file: ["/magento/build.yml", "/magento/deploy.yml", "/magento/verify.yml"]
# Override only what differs from the template defaults
variables:
DEPLOY_HOST: "${PROD_DEPLOY_HOST}" # From GitLab CI/CD variables
DEPLOY_PATH: "/var/www/shop"
MAGENTO_LOCALE: "de_DE en_US" # Multi-locale override
THEME_PATH: "app/design/frontend/Mironsoft/default"
# Extend the template deploy job with a project-specific step
deploy:production:
extends: .deploy:production:base # From included template
after_script:
# Notify project-specific Slack channel after deployment
- |
curl -X POST "${SLACK_WEBHOOK_URL}" \
-d "{\"text\": \"Deployed ${CI_COMMIT_TAG} to production\"}"
7. Template versioning and breaking changes
Templates referenced by multiple projects must be versioned. Without versioning, every change to a template immediately affects every project that includes it. That is unacceptable for breaking changes, for example renaming a hidden job or adding a required variable, because projects could break without any warning.
Semantic versioning (SemVer) works well for CI templates too: patch versions for bug fixes, minor versions for new optional features, major versions for breaking changes. Projects pointing at a major-version tag automatically receive all compatible improvements, but have to migrate deliberately at the next major release. The template repository pipeline automatically creates tags on merges to main to simplify the versioning process.
8. Comparison: copied pipelines vs. template-based pipelines
The difference between copied and template-based pipelines shows up most clearly when a security issue or a best-practice update needs to be rolled out across every project. With copied pipelines, that is a manual process across every repository. With template-based pipelines, it is a single change in the template repository.
| Aspect | Copied pipelines | Template-based pipelines | Benefit |
|---|---|---|---|
| Rolling out a security fix | Edit N repositories | 1 template update | Drastically less effort |
| Consistency across projects | Diverges over time | Centrally enforced | Same standard everywhere |
| Setting up new projects | Copy, adjust, test | include plus setting variables | Minutes instead of hours |
| Traceability | Each repo on its own | Central changelog | Auditable and traceable |
| Project-specific adjustments | Directly possible | Via extends and variables | Flexible, but controlled |
9. Pitfalls of template reuse
A common pitfall is unintentionally overriding template variables with project variables. If a template defines a variable as required and the project sets a variable with the same name but a different value, the project value silently wins, with no error message. This can lead to silent malfunctions that are hard to debug. A naming convention for template-internal variables (for example, a _TMPL_ prefix) reduces the risk of collisions.
A second pitfall involves referencing hidden jobs from templates. Hidden jobs start with a dot (for example .build:magento:base) and are never run directly. If a project extends a job with extends: .build:magento:base and the template job gets renamed, the project's pipeline fails with an unhelpful error message. Breaking changes in templates must therefore always be communicated with a major version bump and a clear changelog.
A third pitfall is GitLab's caching of template files. With the include: remote variant, GitLab caches the external URL. With include: project using a branch ref (instead of a tag), the template file is read from the repository at the moment the pipeline runs, which gives you the expected freshness but also means an invalid commit in the template repository immediately blocks every dependent project's pipeline.
10. Summary
Reusing GitLab CI templates across multiple Magento projects is an investment that pays off starting with the second project. The include: project system lets you define a shared build, test and deploy standard centrally and deliver it to every project in a controlled way through version tags. extends allows project-specific overrides without full job copies. Variable defaults in the template and overrides in the project repository keep the shared standard cleanly separated from project-specific details.
The template repository itself needs its own pipeline with lint checks and semantic versioning. Breaking changes must be communicated, and projects migrate to new major versions deliberately. With this system, a security fix in the deploy script becomes a single commit in the template repository instead of ten commits across ten repositories.
GitLab CI Templates: The Essentials at a Glance
include: project
Pulls templates from a central repository. Set ref to a tag for stability control. Access governed by GitLab permissions.
extends
Derives jobs from template definitions. Deep merge for dictionaries. Only differing properties need to be overridden.
Versioning
SemVer for template tags. Patch for bug fixes, minor for new features, major for breaking changes with an explicit changelog.
Project overrides
Variable overrides for configurable differences. extends for structural deviations. No direct editing of template files.