Chapter 19Lesson 03~250 minutes

Parent-Child Pipelines, Multi-Project Pipelines, Trigger Tokens, and Pipeline Composition: Configuration, Design Choices, and Tradeoffs

Choose pipeline boundaries, triggering identity, status coupling, inputs, variable forwarding, and artifact strategy according to ownership, reliability, and security—not YAML convenience.

ArchitectureCompositionToken choiceStatus couplingVariable forwardingTradeoffs

Learning objectives

  • Choose a monolithic, parent-child, or multi-project pipeline based on ownership and dependency boundaries.
  • Compare CI_JOB_TOKEN and trigger tokens by lifetime, scope, observability, and operational burden.
  • Choose loose versus mirrored status propagation intentionally.
  • Control inputs/variables and artifact flow across downstream boundaries.
  • Evaluate maintainability, security, reliability, compatibility, performance, and cost together.
Availability baseline (verified 2026-08-22 against current GitLab 19.3 documentation). Parent-child pipelines, multi-project pipelines, trigger jobs, trigger:strategy: mirror, downstream inputs, pipeline trigger tokens, the pipeline triggers API, CI_JOB_TOKEN cross-project access controls, and the job-token scope API are available on GitLab Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated. The mandatory path uses one disposable Free project and a local child configuration. A second disposable project is optional. Fetching upstream artifacts with needs:pipeline:job or needs:project is currently Premium/Ultimate, so cross-pipeline artifact retrieval is taught with a fixture/read-only extension rather than required for completion.

1. Design principle: split pipelines where responsibility splits

Pipeline decomposition is useful when it reflects an independent unit: component configuration in a monorepo, an independently owned downstream project, or an execution boundary with distinct permissions/runners. It is harmful when used as decorative modularity around jobs that still share one lifecycle and one owner.

Every downstream boundary adds another configuration compile, pipeline object, status edge, permission check, and debugging surface. Optimize for clear ownership and causality, not the maximum number of pipelines.

2. Monolithic pipeline versus parent-child decomposition

Question Monolithic / DAG Parent-child
Source identity One project/ref/SHA Same project/ref/SHA across parent and child.
Configuration namespace One merged pipeline config Separate child config compiled into a downstream pipeline.
Graph size Can become crowded Component graphs can be isolated behind trigger jobs.
Status Direct job graph Requires deliberate trigger strategy if child must control upstream.
Best fit Tightly coupled jobs and shared lifecycle Monorepo components with distinct CI workflows but shared source revision.
Risk Large config namespace/cognitive load Hidden status/variable/artifact assumptions and excessive hierarchy.

If two jobs merely need early start order, use needs. Parent-child should solve configuration/lifecycle decomposition, not replace a DAG.

3. Parent-child versus multi-project orchestration

Dimension Parent-child Multi-project
Project boundary None Yes
Ref/SHA Same as parent Target project/ref determines downstream SHA
Secrets/variables Same project policy with downstream config effects Downstream project owns separate variable/protection policy
Runners Same project runner eligibility model Downstream project runner policy
Ownership Usually one repository/team Useful when another team/project owns lifecycle
Nesting Two child levels maximum Independent; no child nesting limit relationship
Visibility Child appears under parent details Also appears in downstream project pipeline list

Do not use multi-project pipelines to hide ownership. The upstream pipeline page exposes downstream project name and status, so public-to-private composition may reveal metadata even when the downstream project itself is private.

4. CI_JOB_TOKEN versus pipeline trigger token

Property CI_JOB_TOKEN Pipeline trigger token
Creation Automatic per job Created explicitly by Maintainer/Owner
Lifetime Job lifetime; invalid after job ends/erasure Persistent until revoked
Authority Pipeline user permissions + restricted job-token API surface + target allowlist Impersonates creator’s project access/permissions for trigger endpoint
Cross-project control Target allowlist; optional fine-grained endpoint permissions Target project token itself; protect as long-lived credential
Audit clue Job/pipeline and job-token authentication log Triggered job shows token prefix/triggered label
Best fit GitLab CI job orchestrating GitLab resources External integration that lacks a GitLab job identity

Inside GitLab CI, prefer the short-lived job token when it satisfies the operation. A persistent trigger token should exist because an external boundary requires it, not because it was easy to copy.

5. Loose trigger versus mirrored status

Ask a causal question: Can the upstream pipeline truthfully declare success if the downstream pipeline later fails?

  • If yes, default trigger behavior may be correct: the upstream only promises that downstream creation was requested successfully.
  • If no, use strategy: mirror so the trigger job waits and exactly reflects downstream status.

Mirroring improves correctness but lengthens the upstream critical path. That can increase runner/compute duration in later stages or block unrelated work. Status coupling should follow the real release invariant.

6. Inputs versus variables: configuration contract versus ambient state

Use typed inputs for deliberate downstream configuration such as target channel, component name, or test mode. Use variables for runtime environment behavior that genuinely belongs in the job environment. Avoid forwarding all pipeline variables just because a child “might need them.”

trigger:forward makes forwarded values high-precedence pipeline variables downstream. That power can silently override downstream defaults. Keep forwarding lists conceptually small and documented.

7. Secrets should terminate at their owning project

A multi-project trigger should usually pass intent (“deploy artifact digest X to staging”) rather than a credential. The downstream project should obtain its own environment-scoped or federated credential according to its policy. This keeps the upstream project from becoming a secret-distribution hub.

Masked is not portable trust. Passing a masked variable from upstream to another project does not transfer the masking configuration. A downstream job can print the raw value if it receives it as an ordinary variable.

8. Artifact design: pass identity first, bytes only when required

Current upstream artifact fetching with needs:pipeline:job and needs:project is Premium/Ultimate. Even when available, a downstream pipeline should know exactly which producer pipeline/job/ref and checksum it expects.

For a Free-compatible architecture, prefer immutable package/container/release identities in the appropriate registry when that course boundary is reached, or let each downstream pipeline build its own synthetic output for learning. Do not abuse cache as cross-project artifact transport.

9. Static child config versus generated child config

Chapter 12 introduced dynamic child configuration. Static child files are easier to review and reason about. Generated child YAML can model large change-dependent matrices, but the generator becomes a compiler for executable infrastructure: unreviewed input can create unexpected jobs, images, scripts, and credentials access.

Prefer static composition until a measurable configuration-generation problem exists.

10. Ref policy for multi-project pipelines

A multi-project trigger defaults to the downstream project's default branch unless a branch/tag is specified. That means upstream commit identity does not determine downstream source identity. Pin/choose the downstream ref explicitly when reproducibility requires it, and avoid ambiguous names where a branch and tag share the same name.

11. Trigger-loop prevention belongs in architecture

A common pipeline storm appears when Project A triggers B, B triggers A, or a webhook responds to pipeline events by creating another pipeline. Prevent loops with explicit ownership direction and workflow:rules/rules keyed to pipeline sources.

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "pipeline"'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "trigger"'
      when: never
    - if: '$CI_COMMIT_BRANCH'

normal_branch_job:
  script: echo "ordinary branch pipeline only"

Do not blindly copy this rule into every downstream project; a project intended to accept multi-project pipelines obviously needs pipeline source. The point is to state which sources are accepted rather than relying on accidental defaults.

12. Hierarchy limits versus maintainability limits

The default hierarchy limit of 1000 downstream pipelines and two child nesting levels are product limits, not recommended architecture sizes. Long before those numbers, humans can lose causal visibility. Track fan-out, trigger count, downstream critical path, and failure triage time.

13. Worked decision table

Requirement Choice Justification
Monorepo backend/frontend test configs, same commit Parent-child + mirror Same SHA is essential; parent must fail if component validation fails.
App project requests deployment from separately governed platform project Multi-project + typed inputs + mirror Ownership/runner/secrets boundary is real; upstream needs deployment outcome.
Nightly external appliance invokes GitLab Trigger token, minimally scoped operational integration No GitLab job context exists; token must be stored/revoked and accepted source guarded.
GitLab job calls target project API CI_JOB_TOKEN + target allowlist/fine-grained permission Short-lived credential with explicit inbound trust.
Parent wants child artifact bytes on Free tier Redesign mandatory path; pass identity/metadata or use same-pipeline artifact flow Cross-pipeline upstream artifact fetch is Premium/Ultimate.
Pipeline decomposition only saves 10 lines of YAML Stay monolithic / use include or DAG Extra pipeline object/status boundary is not justified.

14. Production composition record

Pipeline edge: app -> deploy-platform
Source project/ref policy: app/* -> protected main/tag only
Trigger mechanism: trigger:project
Downstream ref: main (explicit)
Status strategy: mirror
Accepted inputs: artifact_digest, environment (typed)
Forwarded variables: none by default
Secrets: downstream-owned only
Runner boundary: deploy-platform protected runners
Who may modify edge: app Maintainers + CODEOWNERS policy
Who may run downstream: target project permission policy
Failure owner: platform delivery team
Cleanup/revocation: job-token allowlist reviewed quarterly; no trigger token

15. Anti-patterns

  • “More child pipelines means more modular.” It may only mean more control-plane objects and harder debugging.
  • “Allowlist means access granted.” It does not create user membership/roles.
  • “Mirror means artifacts propagate.” Status and bytes are separate.
  • “Masked upstream variable stays masked downstream.” Masking metadata does not safely cross ordinary multi-project forwarding.
  • “Trigger token is just another variable.” It is a persistent credential that impersonates project access and must be revocable/audited.

Knowledge check

When should parent-child be preferred to multi-project?

What question decides whether strategy: mirror is appropriate?

Why is CI_JOB_TOKEN usually preferable inside GitLab CI?

Why should a deployment project own deployment secrets?

What is the risk of broad trigger:forward?

Summary

Choose composition boundaries where ownership or configuration lifecycle truly changes. Parent-child preserves project/ref/SHA; multi-project preserves project autonomy. Prefer typed inputs, short-lived job tokens, explicit target allowlists, and strategy: mirror only when downstream outcome is part of upstream correctness. Keep secrets downstream-owned and treat artifact, status, and variable propagation as three different mechanisms.

Official references

Next lesson

Diagnose downstream failures without weakening trust

Lesson 4 preserves pipeline/trigger/API evidence and repairs missing allowlists, token leaks, status mistakes, over-forwarded variables, and pipeline loops with the least destructive change.

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.