Chapter 15Lesson 01~170 minutes

Multi-Project Pipelines, Downstream Triggers, Cross-Project Dependencies, and Platform-Oriented Delivery: Concepts, Architecture, and Mental Model

Cross-project delivery changes the trust boundary. This lesson builds the mental model from an upstream source pipeline through downstream project/ref selection, trigger authorization, CI_JOB_TOKEN allowlists, explicit inputs, status coupling, cross-project evidence, and independent source-SHA verification.

Multi-project pipelinesCI_JOB_TOKENDownstream triggersAuthorizationPlatform delivery

Learning objectives

  • Explain multi-project pipelines as separate project/repository pipeline records connected by an explicit trigger relationship.
  • Trace upstream project/ref/SHA → trigger authorization → downstream project/ref/SHA → jobs/evidence without assuming the source revisions are identical.
  • Distinguish trigger-job creation status from downstream pipeline status and use strategy: mirror when status coupling is required.
  • Explain CI_JOB_TOKEN lifetime, user-derived permissions, target-project allowlists, and why allowlisting is not membership.
  • Separate Free-tier cross-project API authorization from Premium/Ultimate cross-project artifact conveniences such as needs:project.

1. The practical problem: project boundaries are delivery boundaries

Chapter 14 decomposed one repository into parent and child pipelines. Parent and child pipelines remain in the same project and share the same ref and source SHA. Multi-project pipelines are different: the downstream pipeline belongs to another project with its own repository, permissions, protected refs, variables, runners, artifacts, environments, and source history.

The practical platform problem is therefore not “how do I start another pipeline?” It is “how do I prove exactly which downstream project/revision ran, who was authorized to start it, what non-secret data crossed the boundary, what status the upstream actually observed, and which evidence belongs to which project?”

2. Keep the cross-project states separate

State Question to prove Typical evidence
Upstream source Which project/ref/SHA initiated delivery? Upstream project ID/path, pipeline ID/source, ref, CI_COMMIT_SHA
Trigger contract Which downstream project/ref and strategy were requested? Compiled YAML, trigger job, inputs/forward policy
Authorization Why was creation/access allowed? Triggering user permissions, target allowlist, token type/scope
Downstream source Which exact downstream SHA actually executed? Downstream project ID/path, pipeline ID/source, ref, CI_COMMIT_SHA
Runtime Which downstream jobs/runners executed? Job IDs, runner/executor/image, traces
Cross-project data What non-secret values or artifacts crossed? spec:inputs values, API response metadata, artifact identity
Status Did upstream only create downstream, or mirror its result? Trigger strategy + trigger/downstream statuses
Governance Who owns compatibility and rollback? Protected ref/tag policy, owners, version/runbook

3. Mental model: upstream identity → authorization → downstream identity → evidence

An upstream pipeline begins with its own project, pipeline source, ref, and SHA. A trigger job asks GitLab to create a pipeline in another project. GitLab checks the triggering user's permission to create that downstream pipeline. The downstream project resolves the requested ref to its own SHA and compiles its own CI configuration.

Only after that boundary is crossed do downstream jobs queue and execute. Their artifacts, reports, environments, and job tokens belong to the downstream project context. If the downstream later calls an upstream API with CI_JOB_TOKEN, that is a second authorization decision: the target project must permit the source project and the triggering user must already have sufficient access.

flowchart TD A[Upstream project / ref / SHA] --> B[Trigger job + downstream project/ref] B --> C{Permission to create downstream?} C -->|yes| D[Downstream project resolves ref to SHA] D --> E[Downstream config + jobs] E --> F[Runner execution + evidence] F --> G{Cross-project API/artifact access?} G -->|job token + allowlist + user permission| H[Authorized target resource] G -->|missing authorization| I[403/404 evidence] E --> J[Downstream status] J --> K[Trigger status: creation-only or mirror]

4. A trigger job is orchestration metadata, not a runner job

A YAML multi-project trigger uses trigger:project. Trigger jobs do not consume a runner. GitLab itself attempts to create the downstream pipeline. A trigger job that remains pending for an unusual time is therefore not first diagnosed by adding runner capacity.

trigger-downstream:
  stage: orchestrate
  trigger:
    project: platform-lab/ch15-downstream
    branch: lab-v1

With default strategy, the trigger job passes once the downstream pipeline is created successfully. It does not mean the downstream jobs later succeeded. That distinction becomes central in release orchestration.

5. Requested ref and executed SHA are different evidence

A multi-project trigger typically names a branch or tag. GitLab resolves that ref in the downstream repository. A branch is moving state; even a tag can be moved unless governance prevents it. Therefore the upstream must record the requested downstream ref and the downstream must independently report its actual CI_COMMIT_SHA.

For controlled delivery, prefer a protected/versioned release tag or another governed stable ref, then verify the resolved downstream SHA. The production claim is “downstream pipeline 812 ran project X at SHA Y from ref Z,” not merely “we triggered stable.”

6. Pipeline-source semantics change across the project boundary

Jobs created by a multi-project trigger see CI_PIPELINE_SOURCE=pipeline. This lets a downstream project distinguish orchestration calls from its normal push, schedule, or merge-request pipelines.

accept-platform-trigger:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "pipeline"'
  script:
    - printf 'project=%s\nref=%s\nsha=%s\nsource=%s\n'         "$CI_PROJECT_PATH" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" "$CI_PIPELINE_SOURCE"

Do not reuse the parent-child condition parent_pipeline here. That value belongs to child pipelines in the same project.

7. Inputs are the preferred downstream configuration contract

Current GitLab supports downstream pipeline inputs. Define the downstream contract with spec:inputs, then provide those inputs from the upstream trigger. Inputs can carry non-secret control data such as an upstream SHA, release channel, or producer project ID while providing validation and documentation.

# downstream .gitlab-ci.yml
spec:
  inputs:
    upstream-sha:
      type: string
      regex: '^[0-9a-f]{40}$'
    release-channel:
      options: [test, canary]
---
verify-contract:
  script:
    - echo "release-channel=$[[ inputs.release-channel ]]"
    - echo "upstream-sha=$[[ inputs.upstream-sha ]]"

Inputs are not secret storage. Never put passwords, personal tokens, or cloud keys in an input merely because the downstream pipeline accepts it.

8. Variable forwarding can silently widen the contract

Forwarded variables become downstream pipeline variables with high precedence. That can override downstream project defaults and make behavior hard to audit. Masking metadata also does not transfer safely when ordinary YAML variables are passed between projects.

Prefer explicit inputs. If variable forwarding is required, configure trigger:forward narrowly and use inherit:variables deliberately. Never forward a broad environment simply for convenience.

9. CI_JOB_TOKEN is temporary authority, not a universal cross-project key

GitLab creates CI_JOB_TOKEN when a job starts and revokes it when the job finishes. It has fewer resource capabilities than a PAT and its effective access is tied to the user who triggered the pipeline. For cross-project access, the target project normally must allowlist the token's source project or group.

Adding the source project to an allowlist does not grant its users membership or permissions in the target. Both conditions matter: target authorization policy and the triggering user's existing project permission.

10. Think from the target project when designing an allowlist

If a job in downstream needs to read metadata from upstream, the upstream project is the target resource. Upstream therefore allowlists the downstream project (or a narrowly chosen parent group). Reversing this relationship is a common cause of 403/404 failures.

Request Token source Target project that owns resource Allowlist direction
Downstream reads upstream commit metadata downstream job upstream upstream allowlists downstream
Upstream job calls downstream API upstream job downstream downstream allowlists upstream
Trigger keyword creates downstream pipeline GitLab trigger relationship downstream checked through user permission/trigger semantics, not artifact allowlist alone

11. Cross-project artifact transfer is a separate capability and tier decision

Do not infer artifact access from pipeline triggering. Current needs:project is Premium/Ultimate and fetches artifacts from up to five jobs in another project. It retrieves the latest successful specified job for the given ref and does not wait for a currently running pipeline on that ref. CI_JOB_TOKEN-authenticated Job Artifacts API cross-project downloads are also Premium/Ultimate.

The mandatory Free path in this chapter therefore proves allowlisted cross-project API metadata access. If you have the required tier, an optional exercise adds artifact transfer only after you have captured exact producer project/ref/SHA/job identity.

12. Creation success and downstream success are different states

Default trigger behavior answers “was the downstream pipeline created?” When the upstream must block on and represent the downstream result, use strategy: mirror. Current GitLab recommends mirror rather than depend for new designs because mirror makes the trigger job status match the downstream status.

release-validation:
  stage: orchestrate
  trigger:
    project: platform-lab/ch15-downstream
    branch: lab-v1
    strategy: mirror

13. Read-only inspection sequence

  1. Record upstream project path/ID, pipeline ID/source, ref, and SHA.
  2. Inspect expanded upstream configuration: downstream project, requested ref, strategy, inputs, and forward policy.
  3. Record trigger job ID/status and downstream pipeline ID.
  4. In downstream, record project path/ID, pipeline source, requested ref, actual SHA, and job graph.
  5. For any cross-project API access, record token type (never value), source project, target project, target endpoint, HTTP status, and allowlist state.
  6. Keep artifact/report/deployment evidence tied to the project/pipeline/job/SHA that produced it.

14. Common wrong models

  • “The two pipelines run the same commit.” Different projects have independent repositories and SHAs.
  • “Green trigger means green downstream.” Not without a status-coupling strategy such as mirror.
  • “Allowlisting grants access.” It permits job-token use but does not create project membership or user permissions.
  • “Forwarding variables is equivalent to typed inputs.” It is broader, has different precedence, and can create secret/logging hazards.
  • “needs:project waits for the pipeline I just triggered.” It is artifact retrieval from a specified project/ref/job and does not inherently wait for an in-progress pipeline.

Knowledge check

What does a default multi-project trigger job prove when it passes?

What pipeline source do jobs in a multi-project downstream pipeline see?

If downstream needs to call an upstream API with its CI_JOB_TOKEN, which project configures the allowlist?

Why record the downstream SHA if the trigger names a tag?

Why is needs:project not part of the mandatory Free lab?

Next lesson

Guided hands-on workflow and core operations

Build the two-project lab, pass typed non-secret inputs, preserve a denied job-token request, then prove narrow authorization.

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. Multi-project trigger syntax, downstream inputs/forwarding, CI_JOB_TOKEN permissions, allowlist controls, artifact APIs, and needs:project are version-sensitive. Re-check the GitLab version deployed on Self-Managed or Dedicated instances before relying on newer behavior.

  • Downstream pipelines — multi-project triggers, downstream source values, trigger strategies, inputs, variable forwarding, and cross-project artifact patterns.
  • CI/CD job token — token lifetime, user-derived permissions, cross-project allowlists, security, and current allowlist controls.
  • Fine-grained job-token permissions — optional endpoint-level restrictions for allowlisted projects/groups.
  • Trigger pipelines with the API — trigger tokens, CI_JOB_TOKEN multi-project API triggers, source semantics, and revocation guidance.
  • CI/CD YAML syntax reference — trigger, trigger:strategy, trigger:forward, needs:project, and current tier constraints.
  • Job Artifacts API — artifact download by job/ref and the current tier requirement for cross-project job-token downloads.
Current behavior used by this chapter: Multi-project downstream pipelines are available across GitLab offerings. The user who starts the upstream pipeline must also be authorized to start a pipeline in the downstream project. Jobs in a multi-project downstream pipeline see CI_PIPELINE_SOURCE=pipeline. By default, a trigger job succeeds when GitLab successfully creates the downstream pipeline; strategy: mirror makes the trigger job track downstream status, while strategy: depend is not recommended for new designs. Downstream inputs are preferred over broad variables when defining a configuration contract. CI_JOB_TOKEN exists only while a job runs; target projects restrict cross-project token use with an allowlist, and allowlisting does not grant project membership. Current allowlists support up to 200 group entries and 200 project entries. needs:project and CI_JOB_TOKEN-authenticated Job Artifacts API downloads across projects are Premium/Ultimate features, so this chapter's mandatory path uses Free-tier allowlisted API metadata access and treats cross-project artifact transfer as optional/simulated.

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.