Parent-Child Pipelines, Multi-Project Pipelines, Trigger Tokens, and Pipeline Composition: Concepts, Architecture, and Mental Model
Compose GitLab pipelines without losing source identity, status semantics, authorization boundaries, or control over what configuration and data crosses each edge.
Learning objectives
- Distinguish parent-child pipelines from multi-project pipelines by project, ref, identity, and trust boundary.
-
Separate a trigger job,
CI_JOB_TOKENAPI trigger, and pipeline trigger token by authentication and status semantics. -
Explain default trigger-job status versus
strategy: mirrorand whystrategy: dependis no longer recommended. - Use inputs and controlled variable forwarding without accidentally exporting sensitive state.
- Identify which artifact flows remain tier-gated and keep the mandatory design Free-compatible.
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. The practical problem: one pipeline eventually stops being one delivery boundary
Chapters 10–18 treated a pipeline as a coherent execution graph inside one project. That model works until a repository grows into a monorepo with independently testable components, or until one product is split across application, infrastructure, packaging, and deployment projects owned by different teams.
The hard problem is not merely “start another pipeline.” The moment one pipeline creates another, you must answer four questions precisely: where does the downstream configuration live, which commit/ref does it represent, whose permissions and secrets apply, and does downstream failure change upstream status?
GitLab calls any pipeline started by another pipeline a downstream pipeline. The two main composition forms have deliberately different boundaries.
2. Mental model: compose configuration first, execution second
flowchart TD U[Upstream pipeline Project A / SHA A] --> T[Trigger job GitLab control plane] T -->|include local/project/template| C[Child pipeline Project A / same ref + SHA] T -->|trigger project B| M[Multi-project pipeline Project B / selected ref + SHA B] J[CI_JOB_TOKEN job lifetime] -->|API trigger if authorized| M K[Pipeline trigger token long-lived until revoked] -->|Triggers API| Q[Triggered pipeline source=trigger] C --> RC[Child runners / variables / jobs] M --> RM[Project B runners / variables / permissions]
The trigger job is a GitLab control-plane object; it does not
itself need a runner. Parent-child composition stays in the same
project and therefore the child runs under the same project, ref,
and commit SHA. Multi-project composition crosses into another
project, whose configuration, runner fleet, variables, protected
resources, and membership policy become a new trust boundary. A
pipeline trigger token is a separate API credential and should not
be confused with the YAML trigger keyword.
3. Parent-child pipelines: split configuration without changing project/ref/SHA
A parent pipeline triggers a child pipeline in the same project. Current GitLab guarantees that the child runs under the same project, ref, and commit SHA as the parent. That makes parent-child composition a strong fit for monorepos: one top-level pipeline decides which component pipelines to instantiate while all components still describe one source revision.
Child jobs see CI_PIPELINE_SOURCE=parent_pipeline.
Child pipelines are shown from the parent pipeline details rather
than as ordinary top-level entries in the project pipeline list. The
current nesting limit is two child levels below the parent, and one
child trigger can compose up to three configuration files.
component_a:
trigger:
include:
- local: .gitlab/ci/component-a.yml
strategy: mirror
The include determines which child configuration GitLab
compiles. strategy: mirror determines whether the
trigger job waits for and mirrors the child pipeline status.
4. Multi-project pipelines: composition crosses ownership boundaries
A multi-project pipeline runs in another GitLab project. The upstream pipeline can choose the target project and ref, and can pass inputs or selected variables, but the downstream project owns its own CI configuration and execution policy.
The user who caused the upstream pipeline to run must have
permission to start pipelines in the downstream project. The
downstream jobs see CI_PIPELINE_SOURCE=pipeline when
they were created by a YAML trigger job or by a
CI_JOB_TOKEN call to the pipeline triggers API.
deploy_component:
trigger:
project: platform/disposable-deployer
branch: main
strategy: mirror
Unlike child pipelines, multi-project pipelines are visible in the downstream project's normal pipeline list and are not constrained by the two-level child nesting limit. That independence is useful, but it increases blast radius: an upstream project may now cause code to execute on another project's runners with another project's variables and target permissions.
5. Trigger jobs are GitLab scheduler objects, not runner jobs
A job with the trigger keyword is called a
trigger job. GitLab itself attempts to create the
downstream pipeline. The trigger job starts as pending while that
creation is attempted, but it does not consume a runner. A trigger
job that stays pending unusually long points to downstream
creation/permission/configuration problems, not to a missing runner
tag.
By default, the trigger job becomes successful as soon as the downstream pipeline is created successfully. The downstream pipeline may fail later without changing that already-successful trigger job.
6. Status coupling: default loose trigger versus
strategy: mirror
| Mode | Trigger job waits? | Status meaning | Use when |
|---|---|---|---|
No strategy |
No | Success means “downstream pipeline was created.” | Fire-and-observe orchestration where downstream outcome should not block upstream stages. |
strategy: mirror |
Yes | Trigger job exactly mirrors downstream pipeline status. | Upstream correctness depends on downstream completion. |
strategy: depend |
Yes | Legacy dependent behavior with edge-case status differences. |
Compatibility only; current GitLab recommends
mirror instead.
|
With mirror, later upstream stages wait for the trigger
job, and therefore wait for the downstream pipeline. Optional manual
jobs downstream do not block completion; blocking manual jobs do.
This is status composition, not artifact composition.
7. Three triggering identities that look similar but are not
| Mechanism | Credential | Pipeline source downstream | Lifetime / trust |
|---|---|---|---|
YAML trigger:project |
GitLab evaluates the upstream user/project permissions | pipeline |
No separately stored trigger secret in YAML; preferred for GitLab-to-GitLab composition when it fits. |
CI_JOB_TOKEN + triggers API |
Ephemeral job token | pipeline |
Exists only while the job runs; cross-project access normally requires target allowlist plus the triggering user’s permissions. |
| Pipeline trigger token + triggers API | Project trigger token | trigger |
Long-lived until revoked; impersonates creator’s project access; appropriate for external integrations when a job token is unavailable. |
Authentication does not erase authorization. A valid
CI_JOB_TOKEN can still be rejected by a target project
that has not allowlisted the source project, or when the pipeline
user lacks target permission.
8. CI_JOB_TOKEN: short-lived identity with explicit
inbound trust
The job token has the same effective user access level as the user who started the job, but its API surface is deliberately narrower than a personal token. By default, cross-project access requires the source project/group to be present in the target project's CI/CD job-token allowlist and the triggering user to already have the required target permissions.
Current GitLab also supports fine-grained job-token permissions for limiting allowed API endpoint families. Treat the allowlist as permission to attempt authentication, not as membership or role assignment.
9. Pipeline trigger tokens: useful external hook, persistent credential
A pipeline trigger token is created by a Maintainer/Owner and impersonates the creator's project access and permissions. A leaked token can create unscheduled pipelines and may cause jobs to receive variables or deploy authority that the creator could exercise.
The lessons never contain a real trigger token. Syntax examples use obvious non-secret placeholders only.
10. Inputs versus forwarded variables
For downstream configuration parameters, current GitLab recommends
inputs when possible. The downstream config
declares spec:inputs, and the trigger supplies
typed/validated values. This is clearer than a broad bag of
environment variables.
# child config
spec:
inputs:
channel:
options: [lab, staging]
default: lab
---
child_job:
script:
- printf 'channel=%s\n' "$[[ inputs.channel ]]"
Variables remain useful, but trigger:forward can
elevate forwarded values to trigger-variable precedence. Forwarding
pipeline variables is off by default; YAML variables are forwarded
by default. Forwarded values do not automatically forward again
through another downstream level unless the nested trigger also opts
in.
11. Do not forward masked secrets as ordinary multi-project variables
GitLab explicitly warns against passing masked variables to a multi-project pipeline through ordinary variable inheritance: the masking configuration is not carried to the downstream project, so the value could appear unmasked in logs. Prefer a downstream project's own scoped secret, an external secret manager/OIDC flow, or another least-privilege design rather than moving a long-lived secret across project boundaries.
12. Status flow and artifact flow are separate
strategy: mirror does not make parent artifacts
magically appear in a child. Current GitLab's upstream-artifact
retrieval with needs:pipeline:job (parent-child) or
needs:project (multi-project) is Premium/Ultimate. The
mandatory Free path therefore proves composition with inputs,
metadata, pipeline status, and independently produced child outputs.
When the paid feature is available, artifact identity still requires
explicit project/pipeline/job/ref selection and permissions. For
merge request pipelines, the correct upstream ref may be
CI_MERGE_REQUEST_REF_PATH, not the source branch name.
13. Read-only inspection before changing composition
PROJECT_ID="12345678"
PARENT_PIPELINE_ID="9001"
# Parent pipeline identity.
glab api "projects/$PROJECT_ID/pipelines/$PARENT_PIPELINE_ID" --jq '{id,project_id,ref,sha,status,source,user:.user.username}'
# Current GitLab 19.2+ route for trigger jobs (older /bridges route is deprecated).
glab api "projects/$PROJECT_ID/pipelines/$PARENT_PIPELINE_ID/trigger_jobs" --jq '.[] | {id,name,status,downstream_pipeline:(.downstream_pipeline|{id,project_id,ref,sha,status})}'
# Child pipelines can be queried by source explicitly.
glab api "projects/$PROJECT_ID/pipelines?source=parent_pipeline" --paginate --jq '.[] | {id,ref,sha,status,source}'
Do not query or print trigger-token values,
CI_JOB_TOKEN, all job variables, or full untrusted
payloads. The control-plane evidence above is enough to prove
identity and status relationships.
14. Current structural limits are architecture signals
The current default pipeline hierarchy can contain up to 1000 downstream pipelines, and parent-child nesting is capped at two child levels. These are not performance targets. If a design approaches them, ask whether the decomposition is expressing real ownership/dependency boundaries or merely multiplying orchestration objects.
15. DevOps connection: composition creates transitive trust
A trigger edge is also a trust edge. It can cause a different configuration to compile, a different runner to execute code, a different variable set to appear, and a different deployment identity to act. Production pipeline architecture should therefore document each edge with project/ref, trigger mechanism, status strategy, allowed inputs, token policy, secret boundary, and owner.
Knowledge check
What does a parent-child pipeline share with its parent?
The same project, ref, and commit SHA. Child jobs report
CI_PIPELINE_SOURCE=parent_pipeline.
What does a default trigger-job success prove?
Only that GitLab successfully created the downstream pipeline. It does not prove the downstream pipeline later succeeded.
Which status strategy is currently recommended when upstream must wait for downstream outcome?
strategy: mirror. Current GitLab marks
strategy: depend as not recommended.
Does adding Project A to Project B’s job-token allowlist grant Project A users a role in B?
No. The user who triggered the job must already have the target permissions; the allowlist only permits the cross-project job-token authentication path.
Why are typed inputs preferable to broad forwarded variables for configuration parameters?
Inputs provide declared types/options/defaults and reduce accidental precedence/secret-forwarding ambiguity.
Does strategy: mirror transfer artifacts from the
parent?
No. Status coupling and artifact retrieval are separate
mechanisms; upstream artifact retrieval uses explicit
needs forms and is currently Premium/Ultimate.
Summary
Parent-child pipelines decompose one project/ref/SHA; multi-project
pipelines cross project ownership and execution boundaries. Trigger
jobs create downstream pipelines without a runner. Default trigger
semantics are loosely coupled, while
strategy: mirror couples status. Use typed inputs for
configuration, restrict variable forwarding, prefer short-lived
job-token flows inside GitLab, and treat trigger tokens as revocable
persistent credentials.
Official references
- GitLab Docs — Downstream pipelines
- GitLab Docs — Pipeline architectures
- GitLab Docs — CI/CD YAML trigger reference
- GitLab Docs — Trigger pipelines with the API
- GitLab Docs — CI/CD job token
- GitLab Docs — Job token scope API
- GitLab Docs — Fine-grained job-token permissions
- GitLab Docs — Pipelines API
- GitLab Docs — Jobs API / trigger jobs
- GitLab Docs — CI/CD inputs
- GitLab Docs — Troubleshooting downstream pipelines
- GitLab Docs — Job artifacts troubleshooting
- GitLab Docs — Pipeline security
- GitLab Docs — Roles and permissions
- GitLab 19.3 — What’s new
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.