GitLab CI Monorepos: Child Pipelines, Matrix Jobs, and Faster Builds

Bits Lovers
Written by Bits Lovers on
GitLab CI Monorepos: Child Pipelines, Matrix Jobs, and Faster Builds

GitLab’s monorepo docs are blunt about it: long pipelines are expensive, and the cure is to stop running work you do not need. That sounds obvious until you look at how many teams still run every test for every commit because it was easier than wiring the rules properly.

GitLab now has enough pieces to make a sane monorepo pipeline. rules:changes decides what should run, child pipelines keep services isolated, and parallel:matrix fans work out without turning the YAML into a wall of copy-paste. Matrix expressions, introduced in GitLab 18.6, let you wire 1:1 dependencies between matrix jobs without hand-writing each one.

The Shape That Works

A monorepo pipeline does not need to be clever. It needs to be selective.

api:
  stage: test
  trigger:
    include: services/api/.gitlab-ci.yml
  rules:
    - changes:
        - services/api/**/*
        - packages/shared/**/*

That pattern is the whole game. If the API changed, run the API pipeline. If only the frontend changed, do not pay for the backend. If a shared package changed, fan out to the services that depend on it. Keep the rest asleep.

For build matrices, GitLab now supports a cleaner 1:1 mapping across stages:

build:
  stage: build
  parallel:
    matrix:
      - OS: [linux, macos]
        ARCH: [amd64, arm64]
  script:
    - echo "build $OS/$ARCH"

test:
  stage: test
  needs:
    - job: build
      parallel:
        matrix:
          - OS: ['$[[ matrix.OS ]]']
            ARCH: ['$[[ matrix.ARCH ]]']
  script:
    - echo "test $OS/$ARCH"

That is the sort of pattern that turns a pipeline from “wait for everything” into “wait for the thing you actually built.”

Why Monorepos Get Slow

The repo is rarely the real problem. The problem is usually clone cost, unnecessary jobs, and pipeline fan-in that turns one changed file into a full-system rebuild. GitLab’s own guidance pushes the same boring fixes every time: keep clones shallow, use child pipelines for unrelated services, rely on rules:changes, and trim the fetch and cleanup work where you can. It is not exciting. It just saves minutes on every run.

The Practical Tuning

Start with GIT_DEPTH. Shallow clones are the default, but in a large monorepo you should still set the value deliberately. Then trim GIT_CLONE_EXTRA_FLAGS and GIT_CLEAN_FLAGS only as far as your jobs allow. Those two settings look boring on paper, but they decide whether a pipeline spends most of its time fetching files or actually running.

Runner capacity is the hard limit. Parallelism only helps when the pool can absorb it. If the runners are scarce, matrix jobs just create a queue with a nicer graph.

The Gotchas

Treat parallel:matrix as an accelerator, not a default. It only helps when the jobs are independent and the runner pool can keep up. Be strict with rules:changes; if a docs-only change wakes up half the repo, the pipeline is doing extra work for no reason.

GitLab gives you anchors, extends, child pipelines, and reusable components. Use them, but do not turn the YAML into a puzzle that only one person can edit safely. The repo itself still matters, and GitLab monorepo performance guidance is about the clone path as much as the YAML. If the repository is huge, pipeline logic alone will not save you.

When To Use It

Use this model when a change in one place genuinely affects more than one service. If someone touches packages/shared, I want the API, worker, and frontend jobs to react. If they only tweak a docs file, I want most of the pipeline to stay asleep.

If the shared code has become a thin utility layer and each service ships on its own schedule, I would split the repo. At that point the monorepo starts acting like a tax instead of a coordination tool, and the pipeline graph becomes the thing people complain about rather than the thing that helps them ship.

If you are migrating from Jenkins or another older CI system, the Jenkins to GitLab CI migration guide is still the right companion read. GitLab CI is happiest when the YAML matches the way the team actually works instead of hiding the logic inside plugins and shell scripts. If you’re still deciding whether GitLab CI is the right fit, the GitHub Actions vs GitLab CI comparison gives a cleaner side-by-side view.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus