Developing Custom GitLab CI Templates for Reuse

Teams grow, and build boilerplate spreads by copy-paste across dozens of repositories. We develop custom GitLab CI Templates turnkey: a single layer of shared boilerplate cuts build setup time many times over and eliminates discrepancies between projects.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Why GitLab CI Templates get copied and pasted across repositories

Copy-paste starts innocuously: a build pipeline from one project is transferred into the configuration of a neighboring repository.

In one project, dependency caching is added; in another, the publishing stage is changed; in a third, the conditions for running checks are adjusted. After a couple of months, this is no longer one template but a dozen similar files with incompatible details.

The cost does not show up immediately. While edits are pointwise, duplication of build settings is unnoticeable, but the first change — a new security check, a switch of the image registry, different trigger rules — turns into a manual tour of all repositories.

Something was forgotten, somewhere it was applied halfway, and environment divergence becomes the norm: the same code builds in one project and fails in another.

Reuse mechanisms in GitLab — include, extends, !reference — do not solve the problem by themselves. Until the template owner, versioning scheme, and project update rule are defined, the sample file again leaves as a copy. Then maintaining many repositories rests on people, not on configuration.

Next we analyze how this is addressed: a single source of build pipelines, versions instead of copies, and a predictable update path for each repository.

A unified layer of build templates: the effect in numbers

Transition to a unified layer of templates is measured not by the feeling "it became cleaner" but by numbers from the history of build pipelines.

Below is what we record before and after implementing a shared template library in GitLab: we take metric snapshots on the same projects so that the comparison is fair.

  • Build time. A typical service builds in 4–6 minutes instead of 20–30: the template pulls in the shared dependency cache and does not reinstall the environment from scratch at every step.
  • Share of manual edits. Edits to build files drop from dozens per month to units — a change is made once in the template and propagates to all projects.
  • Eliminated class of failures. Divergence of tool versions and parameters between repositories disappears: the build stops "working for one engineer and failing for another."
  • Recovery time. Rolling the template back to a previous revision restores a working build in minutes, without manually editing a dozen files and searching for what exactly broke.
  • Maintenance of build pipelines. A new repository connects to the template with a single change in the settings file — instead of copying someone else's pipeline and adapting it.
  • Share of covered checks. Mandatory steps — static analysis, tests, dependency checks — come from the template and cannot be accidentally skipped in an individual project.

Bottom line: the build layer stops being a set of one-off solutions and becomes manageable — it is changed in one place, and the effect is visible across all projects at once.

GitLab CI Templates: comparison of reuse approaches

In GitLab CI, a shared layer of templates is assembled in several ways, and the choice between them is determined by two numbers: how many repositories are under maintenance and how expensive a failed build is. With two or three projects, local build pipelines are cheaper.

Beyond that, editing one rule turns into N identical commits, and rule versions across projects diverge.

Option When it fits Limitations
Local pipelines in each repository 1-3 projects, one team edit is repeated manually, rules diverge
Shared template library by reference many similar services, uniform checks without versioning, an edit changes the build for everyone at once
Versioned templates with a tag reference several teams, compatibility and rollback needed release discipline and support for multiple versions
Hybrid: shared core and local overrides heterogeneous projects with common steps harder debugging: the result must be seen as a whole

The difference between a library and versioned templates is not cosmetic. A library without versioning becomes a point of failure: the first unsuccessful commit stops builds in all projects at once.

A template with a tag reference provides the same reuse but leaves the project the right to update on its own schedule and return to the previous tag.

The criterion is simple: whether release cycles coincide and whether one team works on the templates.

If yes — a shared library is enough; if the cycles differ or there are several groups, an explicit versioned layer is needed, otherwise every update turns into coordination with everyone at once.

From repository audit to library handover: stages of work

The order of work is built so that the decision relies on repository data, not assumptions. First we record the current state, then design the shared layer and test it on a limited group of projects — and only after that does the GitLab CI Templates library go out to all teams.

  1. Repository audit. We go through all projects in the organization, export configurations, and mark duplicates, manual steps, and divergences between teams. The output is a map: where logic repeats and where it contradicts itself.
  2. Inventory of build pipelines. We compile what we found into a table: triggers, stages, cache, artifacts, environment rules. It becomes clear which blocks become a shared template and which remain project-specific.
  3. Designing the template layer. We divide the library into levels: base, language, application. Separately, we record what must be uniform and what a team may override locally without coordination.
  4. Building and versioning. We write the templates and test them for compatibility with already configured builds. Each change gets a version, so updating the library does not break projects that are not yet ready to migrate.
  5. Pilot group of projects. We connect several repositories with different stacks and compare build time, failure rate, and number of manual edits before and after. We analyze discrepancies before handing over to the remaining teams.
  6. Library handover and training. We prepare documentation: how to connect, how to extend, what to do on rollback. We walk teams through the edit process so that changes go through the template, not by copying between repositories.

After handover, the library lives independently: a new project connects to the shared layer in one step, and rules are updated in one place. This is the point at which maintaining build pipelines stops depending on a specific engineer.

What stays with you: artifacts, policies, documentation

Implementation is finished not when the build pipeline has run without errors, but when the team changes it itself, without us.

Therefore the output is not a set of scripts in chat but a versioned repository of GitLab CI templates with descriptions, policies, and edit regulations. Everything resides in your environment and under your accounts; access is granted to standard roles. Below is exactly what we hand over.

  • A template repository with major version numbering: services connect the required branch, and an update does not break other builds.
  • Stage configurations: environment preparation, build, static checks, tests, publishing artifacts and images.
  • Inheritance rules: which parameters a project overrides itself and which it must inherit from the shared template.
  • Access policies: separate roles for editing templates, connecting, and viewing; protection of the main branch and mandatory change approval.
  • Dependency scheme: which service relies on which template version and what will break when the shared file is edited.
  • Build documentation: required variables, connection examples, analysis of common errors and their causes.
  • Change regulations: how to propose an edit, who approves it, how a new version is rolled out, and how to roll back to the previous one.
  • Instructions for troubleshooting a failed build and a recording of the final review with your team — the context stays with you, not with us.

Case study: 40 repositories and one layer of build templates

The project is 40 repositories of one product team, and each had its own build file. Pipelines were copied from a neighboring repository: by the time of the review, the copies had fewer common lines than divergences, and editing one stage meant 40 separate changes.

What it was like

No one could say exactly how one service's build differed from another's. Updating an image or caching rules turned into a raid across all repositories: some edits were forgotten, some were made differently, and the build behaved unpredictably.

Recovering a failed build took about four hours — the engineer first had to find which copy had broken.

What we changed

We moved the shared layer of templates to GitLab CI Templates: stages, cache rules, artifact publishing, and checks live in one set of templates, while the repository retains a short file that connects the required set and overrides parameters. Each service describes only what is truly its own.

Metric Before After
Copies of build pipelines 40 4
Editing a shared stage 40 repositories 1 template
Build time ~18 min ~7 min
Build recovery ~4 hours ~20 min

Duplication of pipelines decreased tenfold, and this is not a one-time cleanup: a new template is connected with one line, so copies will not spread again. A new service gets a working build on the first day instead of a week of copying someone else's file.

How do shared templates differ from per-project pipelines?

In GitLab CI, a shared template describes the invariant — what is the same in most repositories: dependency installation, test runs, artifact build, registry publishing.

The project pipeline adds what depends on the repository: the set of environments, trigger branches, the build matrix, and rollback rules. The boundary between them lies where a rule stops repeating.

The practical criterion is simple: if a rule differs in two out of three projects, its place is in the project pipeline; if it matches everywhere, it is elevated to the shared template.

The template is parameterized — image, stage, artifact name are passed from outside — otherwise it quickly turns into a set of conditions for each repository.

The project pipeline remains a thin layer of a few dozen lines and only connects the shared templates. Then editing the build policy is done in one place, not in forty files, and a non-standard project does not drag the others along.

Common technical questions about shared build templates

Most doubts of technical leaders come down to four questions: whether the shared template will break current builds, how to migrate existing configurations, who is entitled to change the shared code, and how to roll back a change if it has already spread across dozens of projects. We answer substantively.

A template is a regular build pipeline configuration file connected by reference; it provides the base, while project settings remain prioritized.

Will the shared template break our current builds?

The template is connected by a reference to a specific tag and does not rewrite project jobs: inheritance takes the base, while local trigger conditions, dependencies, and variables remain prioritized.

Compatibility is checked on a separate branch — first a run on two or three pilot projects, then rollout to the rest.

How do we migrate an existing configuration to the template?

Incrementally: first we extract the shared build scripts, then stages, cache, and artifacts — each step as a separate change. The old configuration continues to work until the new variant is confirmed by a series of runs, and the switch remains reversible at any step.

Who can change the shared template?

The template repository is closed: the right to change it belongs to a limited circle of engineers, the branch is protected, checks and code owner approval are mandatory. Projects connect the template read-only, so an edit in one of them does not affect the others.

How do we roll back an unsuccessful change?

The reference is tied to a tag, not to a floating branch, so a rollback is returning the tag in the project or releasing a fixed one. There is no need to synchronously edit dozens of configurations and rewrite history.

Let's discuss the task: what we need from you and what we do

To make the estimate realistic rather than "plus or minus an order of magnitude," we need to see the current state of the repositories and understand how your build and deployment processes are structured. Describe the task — we will come back with the template composition, work plan, and scope estimate.

What helps as input:

  • A list of projects and build types: which stacks are used, how many repositories, where the build differs fundamentally and where it differs only in the set of commands.
  • Current configuration files and connected includes — they show duplication of stages and blocks that can be folded into shared GitLab CI templates.
  • Build runners and environments: shared or own, whether there are restrictions on network, cache, or access to external resources.
  • Critical pre-deployment stages: tests, static analysis, image builds, registry publishing — what must pass and what is optional.
  • Secrets handling scheme: where credentials are stored, who has the right to change group-level and individual project variables.
  • Compatibility requirements: which branches and merge rules are supported, whether a separate set of templates is needed for different product types.

After the discussion, we come back with a map of reusable templates and responsibility zones in each, a plan for migrating projects without stopping deployments, the order of stages, and a scope estimate.

Then — prioritization and implementation, starting with the repositories where duplication is most costly.