Skip to content
CloudOps
All prompts
AI for GitLab CI/CD Difficulty: Advanced ClaudeChatGPT

GitLab Parent/Child & Multi-Project Pipeline Design Prompt

Design parent/child pipelines, multi-project triggered pipelines, and downstream pipeline orchestration — `trigger:`, dynamic child generation, cross-project dependencies.

Target user
Platform engineers building complex pipeline workflows
Difficulty
Advanced
Tools
Claude, ChatGPT

The prompt

You are a senior platform engineer who has designed multi-project pipelines and parent/child pipeline workflows in production GitLab environments. You know when triggering downstream is the right answer and when it's just over-engineering.

I will provide:
- The use case: monorepo with per-service pipelines? Cross-project deploy dependency? Dynamic test matrix?
- Current pipeline shape (single `.gitlab-ci.yml` or multiple)
- Constraints: GitLab tier, who owns which project, what should block what
- Symptom (if debugging an existing setup): child pipeline didn't start, parent didn't fail, variables didn't pass, status check stuck pending

Your job:

1. **Decide which mechanism fits**:
   - **Parent → Child pipeline (single project)**: `trigger:include:` from a parent job creates a child pipeline using a separate YAML. Use for monorepos (per-package child pipelines), dynamic generation (parent emits YAML at runtime).
   - **Multi-project pipeline (same instance)**: `trigger:project:` triggers a pipeline in a different project. Use for cross-project deploys, library-to-consumer chains.
   - **API trigger (external)**: another project or tool calls GitLab API with a trigger token. Use for external orchestration.
   - **`needs:project:`** (downstream→upstream artifact pulls): a job in project B pulls artifacts from project A. Use sparingly — couples projects.
2. **For Parent/Child (same project)**:
   - Child pipeline has its own jobs, runs separately, can be inspected per child
   - Parent's `trigger:` job status reflects the child's status (`success`/`failed`)
   - `trigger:include:` accepts file paths in the repo OR `artifact:` (dynamic — generated YAML)
   - Dynamic child generation is powerful for monorepos: parent runs a script to detect which subprojects changed, emits a child YAML triggering only those
3. **For Multi-Project (cross-project)**:
   - Caller's job triggers callee's pipeline
   - `trigger:strategy: depend` makes the caller's job WAIT for the downstream pipeline (otherwise fire-and-forget)
   - Variables: only what you pass explicitly via `trigger:variables:` reaches the downstream; nothing else inherited
   - The triggered pipeline runs on the OTHER project's runners with the OTHER project's secrets — security boundary preserved
4. **Trigger token vs CI_JOB_TOKEN**:
   - **Trigger token**: project-scoped token (Settings → CI/CD → Pipeline triggers); usable from anywhere including external systems
   - **`$CI_JOB_TOKEN`**: implicit token of the running job; can trigger downstream pipelines if the target project allows it (Settings → CI/CD → Job token permissions in newer GitLab; previously project-wide on/off)
   - Prefer `$CI_JOB_TOKEN` for in-instance triggers; trigger tokens for external systems
5. **Dynamic child pipelines (monorepo pattern)**:
   - Parent has a `generate-pipeline` job that emits a `generated.yml` artifact based on what changed
   - A second parent job uses `trigger:include: artifact: generated.yml` to launch the child
   - This is the cleanest monorepo CI pattern in modern GitLab
6. **Variable passing**:
   - **Down to child**: `trigger:variables:` (only these reach the child)
   - **Up from child**: not directly; can use `dotenv` artifacts in the parent pipeline AFTER the child completes (with `trigger:strategy: depend`)
   - For multi-project: same; downstream cannot push variables back without an additional API call
7. **Common anti-patterns**:
   - **Triggering child for every subdir always** instead of `rules:changes:` — defeats the speedup
   - **`trigger:strategy: depend` everywhere** — locks parent to slowest child; consider fire-and-forget when appropriate
   - **Trigger loops**: child triggers parent triggers child — possible, eventually blocked, painful to debug
   - **Over-coupling via `needs:project:`** — project B fails when A's CI breaks; consider package registry instead
   - **Trigger token in YAML** — checked into git, leaks; always use `$CI_JOB_TOKEN` or masked variable
8. **For debugging**:
   - Child not starting → check `trigger:include:` path exists in the parent commit's tree
   - Cross-project trigger failing with 404 → token wrong, or project's job-token allow-list not including the caller
   - Status pending forever → check both projects' pipelines; downstream might be waiting on a manual approval

---

Use case: [monorepo / cross-project deploy / dynamic test matrix / etc.]
GitLab tier: [Free / Premium / Ultimate]
Project layout: [DESCRIBE — monorepo, polyrepo, who owns what]
Symptom (if debugging): [DESCRIBE]
Current `.gitlab-ci.yml` (relevant):
```yaml
[PASTE]
```
Cross-project relationships and triggers: [DESCRIBE]

Why this prompt works

There are 4-5 ways to compose pipelines (DAG, child, multi-project, API trigger, needs:project:) and they’re not interchangeable. Choosing the right one upfront avoids a tangled “everything triggers everything” setup. This prompt forces a use-case-first design.

How to use it

  1. State the use case in plain language before picking a mechanism. “I want X to happen when Y” is the right level.
  2. Map projects and owners. Cross-project pipelines work best when team boundaries are clear.
  3. Decide blocking vs. fire-and-forget upfront (trigger:strategy: matters).
  4. Plan for variable contract: what does downstream need? Pass explicitly.

Mechanism selector

NeedUse
Monorepo: run only changed subproject’s CIDynamic child pipeline (parent generates child YAML)
Library A’s release should trigger consumer B’s CIMulti-project trigger from A → B
External tool kicks off a pipelineAPI trigger with trigger token
Project B needs artifacts from A’s latest mainneeds:project: (couples projects; consider Package Registry)
Same-project, different stages, parallelizeDAG with needs: (no child pipeline needed)
Test matrix that’s known at YAML-write timeparallel:matrix: (no child)
Test matrix discovered at runtimeDynamic child pipeline

Patterns

Parent → Child (static)

# .gitlab-ci.yml
trigger-subproject-a:
  trigger:
    include: subproject-a/.gitlab-ci.yml
    strategy: depend                   # parent waits for child
  rules:
    - changes:
        - subproject-a/**/*

trigger-subproject-b:
  trigger:
    include: subproject-b/.gitlab-ci.yml
    strategy: depend
  rules:
    - changes:
        - subproject-b/**/*

Parent → Child (dynamic, monorepo pattern)

# .gitlab-ci.yml
stages: [generate, trigger]

generate-children:
  stage: generate
  image: alpine:3.20
  script:
    - apk add --no-cache git
    - ./scripts/detect-changes-and-emit-yml.sh > children.yml
  artifacts:
    paths: [children.yml]
    expire_in: 1 hour

trigger-children:
  stage: trigger
  needs: [generate-children]
  trigger:
    include:
      - artifact: children.yml
        job: generate-children
    strategy: depend

Multi-project trigger

# In project "platform/library-a"
deploy-and-notify-consumers:
  stage: deploy
  script: ./deploy.sh
  trigger:
    project: platform/consumer-b
    branch: main
    strategy: depend
  variables:
    UPSTREAM_VERSION: $CI_COMMIT_TAG

needs:project: (artifact pull)

# In consumer project
build:
  needs:
    - project: platform/library-a
      job: build-binary
      ref: main
      artifacts: true
  script:
    - ls -la bin/  # has library-a's binary

(Requires target project to allow the caller in Job token permissions.)

API trigger (external)

curl --request POST \
  --form "token=$TRIGGER_TOKEN" \
  --form "ref=main" \
  --form "variables[VERSION]=1.2.3" \
  "https://gitlab.example.com/api/v4/projects/<id>/trigger/pipeline"

Common findings this catches

  • Child pipeline never startstrigger:include: path doesn’t exist in the parent commit. Test with git show <sha>:subdir/.gitlab-ci.yml.
  • Parent shows green, child failed silently → missing strategy: depend; parent’s trigger: job exits success after launching child.
  • Dynamic-child YAML has syntax error → no error in the parent log; the child pipeline shows as failed at the API level. Lint the generator.
  • Cross-project trigger 404 / Forbidden → Job token permissions in the target project don’t include the source. Allow in Settings → CI/CD.
  • Variables not appearing in childtrigger:variables: only passes the listed ones. Add what you need.
  • Trigger loop (A triggers B triggers A) → cycle protection eventually kicks in, but redesign — usually a misunderstood ownership.

When to escalate

  • Trigger relationships forming a dependency graph nobody understands — diagram it, simplify, prefer Package Registry for library handoffs.
  • Cross-team triggers without clear ownership of failures — establish on-call for downstream pipelines.
  • Trigger tokens leaked to git history — rotate immediately, scrub history, audit pipeline runs since the leak.

Related prompts

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.