for larger monorepos
When a monorepo combines several largely independent sub-projects in a single repository, one monolithic .gitlab-ci.yml quickly becomes confusing and inefficient. Parent-child pipelines with the trigger keyword solve this by giving every sub-project its own, independently executable child pipeline that only runs when something in its area has actually changed.
Table of Contents
- 1. Why a monolithic pipeline fails in a monorepo
- 2. How trigger starts a child pipeline
- 3. strategy: depend and the pipeline status
- 4. Dynamically generated child pipeline YAML as an artifact
- 5. Nesting depth and variable forwarding
- 6. Multi-project pipelines as a related concept
- 7. Practical recommendations for the setup
- 8. Common pitfalls with parent-child pipelines
- 9. Conclusion: parent-child pipelines as a scaling tool
- 10. Summary
- 11. FAQ
1. Why a monolithic pipeline fails in a monorepo
A monorepo bundles several sub-projects, such as multiple microservices, a frontend and a backend, or several libraries, into a single git repository, which makes code sharing and atomic commits across project boundaries easier. But this creates a structural problem for the CI/CD pipeline: a single .gitlab-ci.yml containing all jobs for all sub-projects grows longer and more confusing as the number of sub-projects increases, and without targeted control the entire pipeline runs for every commit across all sub-projects, even if only a single line changed in a single microservice.
This behavior wastes not only CI minutes but also slows the feedback loop for developers considerably, because a small fix in one sub-project still has to wait for tests of entirely unrelated sub-projects to finish. As team size and the number of sub-projects grow, a monolithic pipeline also becomes a shared point of change where multiple teams block each other, since every change to the central .gitlab-ci.yml potentially affects all sub-projects. Parent-child pipelines solve exactly this problem by moving responsibility for each sub-pipeline into its own file that can be maintained and triggered independently of the rest.
2. How trigger starts a child pipeline
The trigger keyword defines a special job type that, instead of a script block, points to another .gitlab-ci.yml file and starts it as an independent child pipeline. From the parent pipeline's perspective, the trigger job looks like a normal job with its own status, but internally it orchestrates an entire second pipeline with its own stages, its own jobs and its own pipeline status. The child pipeline runs completely independently from the other jobs in the parent pipeline and can have its own runners, its own variables and its own stage structure, without the two configurations interfering with each other.
Combined with rules and changes, this solves exactly the monorepo problem: one trigger job per sub-project gets a changes condition that only matches when files in the corresponding subdirectory have changed, so a commit that only touches the payment service triggers exclusively the child pipeline for payment, while the child pipelines for all other microservices are skipped entirely. This selective triggering is the central efficiency gain of parent-child pipelines over a monolithic structure.
trigger_payment_service:
trigger:
include: services/payment/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- services/payment/**/*
trigger_frontend:
trigger:
include: services/frontend/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- services/frontend/**/*
3. strategy: depend and the pipeline status
Without extra configuration, a trigger job is considered successful by default as soon as the child pipeline has been started, regardless of whether the child pipeline itself later fails. This is undesirable for many use cases, because a failed test in the child pipeline then does not automatically mark the parent pipeline as failed. With strategy: depend this behavior changes: the trigger job waits until the child pipeline has fully completed and then adopts its actual success or failure status, so the parent pipeline correctly reflects the real state of all child pipelines.
This setting matters especially when downstream jobs in the parent pipeline depend on several child pipelines, for example a final deployment job that should only run once all affected sub-projects have been tested successfully. Without strategy: depend, that deployment job could incorrectly start even though one of the child pipelines is still running in the background or has even failed. In practice, strategy: depend should therefore be the default for every trigger job whose actual child pipeline success matters for further pipeline logic, and should only be deliberately omitted in rare cases, such as purely informational child pipelines with no blocking effect.
4. Dynamically generated child pipeline YAML as an artifact
Besides child pipeline files that live statically in the repository, GitLab also supports dynamically generated child pipelines, where an upstream job produces a .gitlab-ci.yml file at runtime and provides it as an artifact that then serves as the basis for the trigger job. This is especially valuable in monorepos where the number or structure of sub-projects changes frequently, because the pipeline structure no longer has to be hardcoded, but can instead be derived at runtime from the actual repository state, for example through a script that reads all directories containing a package.json or composer.json and automatically generates one job per sub-project from that.
This technique is often combined with the include: artifact field, which explicitly states that the referenced file is not stored statically in the repository but was produced as an artifact of a previous job. For very large monorepos with dozens of sub-projects, this approach is often the only practical solution, because a manually maintained list of all sub-projects in the parent pipeline quickly becomes outdated and error-prone as new sub-projects are added or old ones removed, whereas a generated pipeline automatically reflects the current state.
generate_pipeline:
stage: prepare
script:
- ./scripts/generate-child-pipeline.sh > generated-pipeline.yml
artifacts:
paths:
- generated-pipeline.yml
trigger_generated:
stage: trigger
needs: [generate_pipeline]
trigger:
include:
- artifact: generated-pipeline.yml
job: generate_pipeline
strategy: depend
5. Nesting depth and variable forwarding
GitLab allows nesting child pipelines up to a fixed depth, where a parent pipeline can trigger a child pipeline that in turn triggers its own child pipeline. These so-called multi-level pipelines are helpful for very large organizations with hierarchically organized sub-projects, but should be used carefully, because every additional nesting level makes things harder to follow and complicates debugging when a failure occurs deep inside a nested child pipeline and has to be traced back through several levels.
For forwarding variables to a child pipeline, trigger offers the variables sub-keyword, which lets you pass specific values to the child pipeline independent of the globally defined variables of the parent pipeline. Additionally, forward can control whether the parent pipeline's pipeline variables and YAML variables should be automatically passed through to the child pipeline, which is especially important for dynamically generated pipelines to avoid sensitive or project-specific variables unintentionally ending up in child pipelines for other sub-projects.
trigger_service_a:
trigger:
include: services/service-a/.gitlab-ci.yml
strategy: depend
forward:
pipeline_variables: true
yaml_variables: false
variables:
SERVICE_NAME: service-a
DEPLOY_ENV: staging
6. Multi-project pipelines as a related concept
Besides parent-child pipelines within the same repository, trigger also supports triggering pipelines in a completely different GitLab project, so-called multi-project pipelines. Instead of an include file, the project: field specifies the path to another project, so a job in project A starts a pipeline in project B, for example when a central infrastructure repository should automatically trigger a deployment after every successful build of an application repository. Multi-project pipelines are conceptually related to parent-child pipelines, but differ in that the two involved pipelines live in separate projects with their own permissions and their own versioning.
For monorepos, the trigger:include variant with child pipelines in the same repository is usually the better fit, because changes to a sub-project's pipeline and its code end up in a single commit and share the same version history. Multi-project pipelines, on the other hand, are better suited for genuinely separate repositories that should not be merged for organizational or security reasons but still need coordinated pipeline execution, for example between a separate infrastructure-as-code repository and several application repositories.
7. Practical recommendations for the setup
For getting started, a clear directory structure is recommended, where every sub-project maintains its own .gitlab-ci.yml in its own subdirectory, while the parent pipeline at the repository root consists only of a list of trigger jobs with matching changes conditions. This separation makes clear who is responsible for which pipeline logic, and lets teams change their own child pipeline largely independently of other teams, without risking merge conflicts in a shared, giant .gitlab-ci.yml.
It is also important to move shared job definitions, such as linting or security scans needed identically across several sub-projects, into a central, reusable file and include it in every child pipeline via include, instead of duplicating them in every sub-project. This way the structure stays consistent despite multiple independent child pipelines, and changes to shared standards only need to be maintained in one place, which is exactly what separates a maintainable setup from CI configuration that grows chaotically once many sub-projects are involved.
8. Common pitfalls with parent-child pipelines
A common mistake is forgetting strategy: depend and then being puzzled why the parent pipeline shows green despite a failed child pipeline. A second common mistake concerns changes conditions that are too narrow or too broad: if only the exact sub-project directory is listed in changes, but a shared library in a different directory that actually affects the sub-project is changed, the corresponding child pipeline incorrectly does not run even though it should, because GitLab performs no automatic dependency analysis across directory boundaries.
A third pitfall concerns error visibility: since a child pipeline appears in the parent pipeline's default view only as a single job, developers have to actively click into the child pipeline to see failure details, which can initially feel unintuitive, especially for new team members. It is worth briefly explaining, either in the trigger job's description or in team documentation, that a red trigger job means the linked child pipeline needs to be checked, instead of searching for the error directly in the parent log.
9. Conclusion: parent-child pipelines as a scaling tool
Parent-child pipelines with trigger are the central tool for keeping monorepos with multiple sub-projects manageable in GitLab CI, because they enable selective triggering via changes conditions, independent child pipeline configurations, and dynamically generated pipeline structures where needed. Configured correctly with strategy: depend and clearly separated responsibilities per sub-project, this approach scales significantly better than a single monolithic pipeline file, even for monorepos with dozens of sub-projects.
The table below compares the key characteristics of monolithic pipelines, static parent-child pipelines and dynamically generated child pipelines, as a decision aid for your own monorepo structure.
| Characteristic | Monolithic pipeline | Static child pipeline | Dynamic child pipeline |
|---|---|---|---|
| Trigger on change | always all jobs | targeted via changes condition | targeted, determined at runtime |
| Maintenance for new sub-projects | central file grows | new file plus new trigger job | automatic from repository structure |
| Failure status propagation | directly visible | correct only with strategy: depend | correct only with strategy: depend |
| Suited for | small, simple repos | monorepos with a stable structure | monorepos with frequently changing structure |
Mironsoft
CI/CD pipelines, zero-downtime deployments and release automation
Deployments that run without downtime and without the nail-biting?
We review existing GitLab pipelines for fragile deployment steps and missing safeguards, then build a release process with zero-downtime deployments, automated checks and a rollback you can actually trust in an emergency.
Pipeline Review
Checking an existing .gitlab-ci.yml for fragility, missing stages and security gaps.
Zero-Downtime Deployment
Building symlink releases, health checks and rollback strategies for Magento stores.
CI/CD Automation
Connecting tests, security scans and deployments into one reliable pipeline.
10. Summary
Child/parent pipelines: The essentials at a glance
Core idea
trigger starts an independent child pipeline per sub-project instead of bundling all jobs into one monolithic file.
Selective triggering
changes conditions per trigger job ensure that only actually affected sub-pipelines run.
Critical setting
strategy: depend ensures the parent pipeline adopts the child pipeline's real success or failure status.
For very large monorepos
Dynamically generated child pipeline YAML as an artifact avoids manually maintained, quickly outdated trigger lists.