Chapter 14Lesson 01~170 minutes

Parent-Child Pipelines, Dynamic Child Pipelines, Generated Configuration, and Monorepo Decomposition: Concepts, Architecture, and Mental Model

Parent-child pipelines let one repository keep one source revision while decomposing CI/CD into smaller pipeline records. This lesson builds the mental model from the parent source SHA and trigger job through static or generated child configuration, child pipeline identity, status/report propagation, and bounded evidence.

Parent-child pipelinesDynamic configurationMonoreposGenerated YAMLPipeline orchestration

Learning objectives

  • Explain parent-child pipelines as multiple pipeline records that share one project, ref, and source SHA but compile and execute separate child job graphs.
  • Trace parent pipeline → trigger job → static/generated child configuration → child pipeline → jobs/reports/artifacts → parent evidence.
  • Distinguish trigger-job creation success from child pipeline status, and explain when strategy: mirror changes that contract.
  • Identify current hierarchy limits, nesting depth, pipeline-source semantics, variable/input forwarding, and dynamic-config constraints.
  • Inspect parent/child IDs, shared SHA, trigger configuration, child source, and non-secret evidence before changing orchestration.

1. The practical problem: one repository can outgrow one pipeline graph

By Chapter 13 you can package reusable pipeline logic as versioned components. That does not solve a different scaling problem: a monorepo may contain many independently owned applications, libraries, and infrastructure folders whose jobs do not need to appear in one giant graph on every change.

Parent-child pipelines split orchestration into multiple pipeline records while preserving one repository and one source revision. The split improves ownership and graph readability only when the boundaries remain traceable. If teams cannot answer which parent SHA created a child, which child configuration was compiled, what was forwarded, and which status the parent is actually observing, decomposition has traded one form of complexity for another.

2. Keep the states separate before changing anything

State Question to ask Evidence
Repository/source Which project, ref, and exact SHA caused the hierarchy? Parent project/ref/SHA and child SHA
Parent configuration Which trigger job and rules selected a child? Expanded parent YAML, trigger job name/rule result
Child configuration Was the child static, merged from includes, or generated as an artifact? Child file/ref or generated YAML artifact + producer job
Pipeline records Which parent and child pipeline IDs exist? Parent pipeline ID, child pipeline ID, relation in graph/API
Forwarded data Which inputs/variables crossed the boundary? Explicit non-secret inputs/variables; trigger:forward policy
Runtime Which child jobs queued and on which runners? Child job IDs/statuses, runner/executor/version
Evidence/output Which reports/artifacts belong to which child job/SHA? Producer job, artifact/report metadata, digest where useful
Governance Who owns the child boundary and how large may fan-out become? Ownership map, hierarchy/job limits, review/rollback policy

3. Mental model: parent source → trigger boundary → child graph

The parent pipeline is created for a project/ref/SHA. Its compiled jobs include one or more trigger jobs. A trigger job does not execute a shell script on a runner; GitLab evaluates its downstream configuration and attempts to create a child pipeline. Static child configuration already exists in the repository. Dynamic child configuration is first generated by an ordinary job, retained as an artifact, then consumed by a trigger job.

flowchart TD A[Parent project/ref/SHA] --> B[Parent compiled pipeline] B --> C[Trigger job + rules] C --> D{Child config source} D -->|Static| E[Repository child YAML] D -->|Dynamic| F[Generator job artifact] E --> G[Child pipeline compile] F --> G G --> H[Child jobs/DAG] H --> I[Reports + artifacts + status] I --> J[Parent hierarchy evidence]

The child is a separate pipeline record, but for a parent-child relationship it runs in the same project, ref, and commit SHA. That shared SHA is the strongest anchor for proving that the parent and child describe one source revision.

4. Same source does not mean same configuration record

Parent and child pipelines share the source SHA, but each pipeline compiles its own configuration. A child can therefore fail configuration while the parent configuration is valid, omit all jobs because child rules do not match, or create a different DAG than the parent author expected.

Current child jobs see CI_PIPELINE_SOURCE=parent_pipeline. They do not see merge_request_event as their pipeline source even when the parent was a merge-request pipeline. Merge-request predefined variables are still available to the child, so child rules that need MR context should test those variables rather than misclassifying the child source.

child-test:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
  script:
    - printf 'source=%s\n' "$CI_PIPELINE_SOURCE"
    - printf 'sha=%s\n' "$CI_COMMIT_SHA"

5. A trigger job proves creation unless you ask for status coupling

By default, a trigger job becomes successful when GitLab successfully creates the downstream pipeline. The child can later fail without changing that already-successful trigger job. That default is useful when the parent should launch independent work, but it is unsafe to interpret the green trigger as “all child work passed.”

Current GitLab recommends strategy: mirror when the trigger job should mirror the child pipeline status. The older strategy: depend remains available but is not recommended for new designs because its status mapping has edge cases.

test-api:
  stage: test
  trigger:
    include:
      - local: ci/children/api.yml
    strategy: mirror

6. Static and dynamic child configuration have different trust surfaces

Child type Configuration identity Primary risk Best evidence
Static local File at the shared repository SHA Stale ownership/rules or hidden include changes Exact source SHA + child file path + expanded child config
Static project/template External config source/ref plus parent SHA Dependency drift/permission assumptions Pinned source/ref + expanded config
Dynamic artifact Generator source + generator inputs + produced YAML artifact Injection/unbounded generation/wrong metadata Generator SHA/job + validated inputs + generated YAML digest + child ID

7. Dynamic child configuration is generated code

A dynamic child pipeline treats a generated YAML artifact as executable configuration. The safe mental model is not “string template convenience”; it is “a compiler emits another program.” Inputs to the generator therefore require the same distrust you would apply to code-generation inputs.

Constrain metadata to an allowlist, reject unknown keys, bound the number of emitted jobs, use fixed job templates, validate the generated YAML, and retain the generated artifact before GitLab consumes it. Never concatenate branch names, MR titles, filenames, API text, or user variables directly into YAML keys or shell commands.

8. Hierarchy limits are reliability controls, not obstacles to remove

Current GitLab allows two child nesting levels and limits the whole downstream hierarchy to 1000 pipelines by default. These limits protect the instance from recursive or explosive creation. A monorepo design that needs hundreds of children per commit should first question its decomposition model, rules, and granularity rather than immediately raising instance limits.

Bound both pipeline count and job count per child. Fan-out also consumes runners, queue capacity, artifacts, logs, API calls, and human attention.

9. Inputs and variables are dataflow contracts across the boundary

Prefer explicit downstream inputs when the child exposes a stable configuration interface. For variables, remember that YAML-defined variables on the trigger job are forwarded by default, while pipeline variables are not forwarded unless trigger:forward:pipeline_variables: true is configured. Forwarded variables have high precedence in the downstream pipeline.

Do not forward secrets “because the child might need them.” Narrow the child’s identity and secret access independently. Forward only non-secret configuration the child contract requires, or use an approved secret mechanism inside the authorized child job.

variables:
  GLOBAL_CONTEXT: "not-needed-in-child"

api-child:
  inherit:
    variables: false
  variables:
    PARENT_PIPELINE_ID: "$CI_PIPELINE_ID"
    MONOREPO_AREA: "api"
  trigger:
    include:
      - local: ci/children/api.yml
    strategy: mirror
    forward:
      yaml_variables: true
      pipeline_variables: false

10. Status, reports, and artifacts cross boundaries differently

Status coupling is controlled by trigger strategy. Report visibility has its own rules: current GitLab can surface supported child-pipeline reports in merge-request views when the parent waits appropriately. Raw artifact transfer is a separate dataflow problem.

For parent-child artifact download, needs:pipeline:job can fetch artifacts from a successful job elsewhere in the same hierarchy, but that capability is currently Premium/Ultimate. The mandatory path in this chapter therefore gives each child everything it needs from repository source or its own jobs and treats cross-boundary artifact fetching as an optional architecture exercise.

11. Read-only inspection sequence

  1. Record parent pipeline ID, source, project, ref, and exact SHA.
  2. Inspect the parent expanded configuration and the trigger job’s rules/include/strategy/forwarding policy.
  3. Identify whether the child configuration is repository-backed or artifact-generated.
  4. For generated YAML, inspect the generator job, exact inputs, artifact path, digest, and YAML content before triggering.
  5. Record child pipeline ID and confirm the same project/ref/SHA plus source=parent_pipeline.
  6. Inspect child expanded configuration/job graph and then runtime job/runner evidence.
  7. Only after those states are known, interpret parent/child status, reports, artifacts, or external side effects.

12. Common wrong models

  • “The child is just another stage.” No. It is a separate pipeline record with its own compiled configuration and job graph.
  • “A green trigger means the child passed.” Only with a status-coupling strategy such as mirror; default success means the child was created.
  • “Dynamic YAML is safe because GitLab validates YAML.” YAML validity does not prevent attacker-controlled job names, scripts, includes, images, variables, or explosive fan-out.
  • “The child is a merge-request pipeline if the parent is.” The child source remains parent_pipeline; use MR variables for MR-specific child behavior.
  • “Forward all parent variables for convenience.” This expands hidden coupling and can leak sensitive or high-precedence data.

Knowledge check

What identity should parent and child always share in a parent-child pipeline?

Why can a green trigger job be misleading?

What value does CI_PIPELINE_SOURCE have inside a child pipeline?

Why must generated child YAML be retained as evidence?

Is needs:pipeline:job required for parent-child pipelines?

Next lesson

Guided hands-on workflow and core operations

Build static and dynamic children in a disposable monorepo, route only changed subtrees, and inspect parent/child identity.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-11. Parent/child pipeline syntax, downstream status behavior, report visibility, hierarchy limits, inputs, and variable forwarding are version-sensitive. Re-check the deployed GitLab version before relying on newer syntax such as strategy: mirror.

Current behavior used by this chapter: parent-child pipelines are available on Free/Premium/Ultimate and GitLab.com/Self-Managed/Dedicated. Child pipelines run in the same project, ref, and commit SHA as the parent and report CI_PIPELINE_SOURCE=parent_pipeline. Nested child pipelines are limited to two child levels; the default pipeline hierarchy limit is 1000 downstream pipelines. One child trigger can combine up to three child configuration files. Dynamic child configuration can come from a generated artifact; the artifact path is interpreted by the GitLab server, and CI/CD variables cannot be used in an include section inside the dynamic child configuration. strategy: mirror was introduced in GitLab 18.2 and is the recommended status-coupling strategy; without a strategy the trigger job succeeds once the child is created. YAML-defined trigger variables forward by default, while pipeline variables do not unless trigger:forward:pipeline_variables: true is set. needs:pipeline:job for cross-parent/child artifact download is currently Premium/Ultimate, so mandatory labs do not depend on it.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.