Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

GitLab logo

Are you looking for ready-to-use pipeline building blocks instead of writing everything yourself? CI/CD Components (GA since GitLab 17.0) are reusable, versioned and documented pipeline blocks. You find them in the CI/CD Catalog, you include them with include:component pointing at a precise version, and the component adds its jobs to your pipeline without you having to write them.

This guide first shows you how to find and use existing components, then how to create your own for your team.

What you will learn

  • Find components in the CI/CD Catalog (gitlab.com or self-hosted)
  • Use a component in your pipeline with include:component
  • Understand the difference between a classic include and components
  • Create your own component with spec:inputs
  • Publish and version a component in the catalog

Prerequisites

This lesson assumes that you can already write a .gitlab-ci.yml (stages, jobs, script) and that you have already used include to reuse YAML coming from another file or another project. If not, the three lessons below cover exactly these basics.

As for permissions, you need a test GitLab project where you can open Settings > General to enable the CI/CD Catalog project setting, then push a Git tag. The official documentation reserves this activation for the Owner role and the publication of a version for the Maintainer or Owner role: a personal project is enough. No particular tier is required: components are available in Free, Premium and Ultimate, on GitLab.com as well as on a self-hosted instance.

In which context?

You write GitLab CI/CD pipelines and you regularly find yourself:

  • Copy-pasting YAML from one project to another for the usual jobs (Docker build, lint, tests, SAST)
  • Searching internal repositories for shared templates without knowing which ones exist
  • Maintaining include: project: files whose expected variables nobody documents

The CI/CD Catalog solves these three problems. It is a registry of pipeline building blocks: search, include, configure.

What is a CI/CD Component?

A CI/CD Component is a reusable pipeline block hosted in a GitLab project. Each component:

  • Declares its parameters through spec:inputs (name, type, default value, description)
  • Is versioned through the project's Git tags (semver)
  • Is published in the CI/CD Catalog of the GitLab instance
  • Is discoverable: search by name, built-in documentation, list of inputs

include vs Components: when to use which?

Both mechanisms reuse YAML, but they do not serve the same need. The table below contrasts them criterion by criterion; read it keeping a single question in mind, how many teams share this code. An include stays a simple file included as is, with no interface or validation, perfect for internal sharing. A component adds a typed interface through spec:inputs, mandatory versioning by Git tag and a card in the catalog, which is only worth it beyond a few projects. The Parameters and Discoverability rows hold most of the difference.

CriterionincludeComponents
ComplexitySimple, include a YAML fileMore structured, dedicated project
ParametersEnvironment variables (untyped)Typed spec:inputs with defaults
VersioningOptional ref:Git tags required for the catalog
DocumentationSeparate README (if you think of it)Built into the catalog
DiscoverabilityYou need to know the pathSearch in the catalog
ValidationNone, the YAML is included as isInputs validated at evaluation
Use caseInternal team sharing, small projectsCross-team sharing, organization standards

Rule of thumb: if it is used by more than 3 projects or more than one team, a component is better suited than an include.

Finding components in the CI/CD Catalog

Where to find the catalog?

The catalog is reachable from GitLab's side menu:

  • GitLab.com: go to Explore > CI/CD Catalog or directly to the URL https://gitlab.com/explore/catalog
  • Self-hosted: side menu Explore > CI/CD Catalog, or https://your-instance.example.com/explore/catalog

For each component, the catalog displays:

  • The project's README (documentation)
  • The available versions (tags)
  • The number of projects that use it
  • The inputs of each template (automatically extracted from spec:)

Official GitLab components

GitLab maintains official components in the gitlab.com/components/ namespace. A few useful examples:

ComponentUsageReference
components/sastStatic application security testinggitlab.com/components/sast@<version>
components/secret-detectionSecret detectiongitlab.com/components/secret-detection@<version>
components/code-qualityCode qualitygitlab.com/components/code-quality@<version>
components/container-scanningDocker image scanninggitlab.com/components/container-scanning@<version>

Using a component

include:component syntax

Include a component in your .gitlab-ci.yml with the include:component directive:

.gitlab-ci.yml
include:
- component: $CI_SERVER_FQDN/components/sast@7.3.2
inputs:
stage: test
stages:
- test
- build
build:
stage: build
script: npm run build

This pipeline includes the official SAST component. The component automatically creates one or more jobs in the stage you indicate, you do not have to write them.

Reference format

The path of a component follows this format:

<instance>/<namespace>/<project>/<template>@<version>
PartMeaningExample
<instance>Your GitLab instancegitlab.com or gitlab.example.com
<namespace>/<project>Group and project of the componentcomponents/sast
<template>File in templates/ (without .yml)Omitted if the project has only one template
@<version>Git tag (semantic version)@7.3.2

Example: 2 components in a pipeline

This example shows the common case where several components coexist with your own jobs. Each - component: block is independent and brings its jobs; here two official components (SAST and secret-detection) plugged into the test stage, plus a build job that you write as usual. The point to remember when reading this YAML: you never define the components' jobs, you only provide their inputs and the host stage. Just make sure the stage passed as an input really exists in your stages list.

.gitlab-ci.yml
include:
# Official SAST scan: creates a sast job in the test stage
- component: $CI_SERVER_FQDN/components/sast@7.3.2
inputs:
stage: test
# Secret detection: creates a secret-detection job in the test stage
- component: $CI_SERVER_FQDN/components/secret-detection@7.3.2
inputs:
stage: test
stages:
- test
- build
build:
stage: build
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
script:
- npm ci
- npm run build

What the pipeline produces: 3 jobs in total, sast and secret-detection (created by the components), plus your build job. The components handle everything (image, script, artifacts), you only provide the inputs.

Component from an internal project

For a component published by your team (not an official component):

.gitlab-ci.yml
include:
# Internal build component: project devops/ci-components-nodejs
- component: $CI_SERVER_FQDN/devops/ci-components-nodejs/build@1.0.0
inputs:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
build_command: "npm run build"
# Test component from the same project (another template)
- component: $CI_SERVER_FQDN/devops/ci-components-nodejs/test@1.0.0
inputs:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
with_coverage: true

Here, the ci-components-nodejs project exposes two templates (build and test), each with its own inputs.

Required vs optional inputs

An input without a default is required. GitLab refuses the pipeline if you do not provide it:

# ❌ Error: 'image' is required (no default in the component)
include:
- component: $CI_SERVER_FQDN/devops/ci-components-nodejs/build@1.0.0
inputs:
build_command: "npm run build"
# OK: 'image' provided, 'build_command' has a default value
include:
- component: $CI_SERVER_FQDN/devops/ci-components-nodejs/build@1.0.0
inputs:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5

To know which inputs exist and which are required, look at the component's page in the catalog, the inputs are listed with their type, their description and their default if any.

Seeing the jobs created by a component

A component can create one or more jobs. To know what will be added to your pipeline:

  1. Read the documentation of the component in the catalog
  2. Use the pipeline editor: go to Build > Pipeline editor and click "Visualize". GitLab resolves the components and shows all the jobs
  3. Look at CI Lint: in Build > Pipeline editor > CI Lint, paste your .gitlab-ci.yml and validate, the result shows the complete configuration after resolution

Creating your own component

Project structure

A component lives in a dedicated GitLab project:

ci-components-nodejs/
├── templates/
│ ├── build.yml # Build component
│ ├── test.yml # Test component
│ └── lint.yml # Lint component
├── README.md # Documentation (displayed in the catalog)
└── .gitlab-ci.yml # Pipeline to test the component itself

Each file in templates/ is an independent component with its own spec:inputs.

Declaring inputs with spec:inputs

The heart of a component is the spec:inputs section, which declares the interface:

templates/build.yml
spec:
inputs:
stage:
default: build
description: "The stage in which the job runs"
image:
description: "Docker image for the build"
build_command:
default: "npm run build"
description: "Build command"
artifacts_path:
default: "dist/"
description: "Path of the produced artifacts"
---
build:
stage: $[[ inputs.stage ]]
image: $[[ inputs.image ]]
script:
- $[[ inputs.build_command ]]
artifacts:
paths:
- $[[ inputs.artifacts_path ]]
expire_in: 30 days

The --- separator splits the inputs declaration from the component's YAML content.

Input types

Declaring a type on each input is not cosmetic: GitLab validates the value when the pipeline is evaluated and refuses a pipeline that passes a string where a number is expected. This validation is what sets a component apart from an include parameterized by environment variables, where any type error goes unnoticed until execution. Without a type, the input is treated as a string. A typed boolean is particularly useful in a rules:if clause, as the example below shows.

TypeDescriptionExample value
stringText value (default type)"node:20"
numberNumeric value30
booleantrue / falsetrue
arrayList of values["docker", "linux"]
templates/test.yml
spec:
inputs:
image:
description: "Docker image for the tests"
type: string
with_coverage:
default: false
description: "Enable the coverage report"
type: boolean
timeout_minutes:
default: 10
description: "Job timeout in minutes"
type: number
---
test:
image: $[[ inputs.image ]]
script:
- npm ci
- npm test
timeout: $[[ inputs.timeout_minutes ]] minutes
rules:
- if: $[[ inputs.with_coverage ]]
variables:
COVERAGE_FLAG: "--coverage"

Publishing in the CI/CD Catalog

Enabling the catalog on the project

A project does not become visible in the catalog just because it contains templates: two conditions must be met. You must first tick the CI/CD Catalog project option in the project settings, then publish a GitLab release attached to a Git tag. Without a tagged release, the component remains usable through its path but does not appear in the catalog search. The README also plays a direct role: it is the page shown to catalog visitors, it acts as documentation.

  1. Go to Settings > General > Visibility, project features, permissions

  2. Enable CI/CD Catalog project (this setting has had this name since GitLab 16.10; it was previously called "CI/CD Catalog resource")

  3. Add a README.md describing your components (this is the page displayed in the catalog)

  4. Create a Git tag for the first version:

    Terminal window
    git tag -a 1.0.0 -m "First release of the Node.js build component"
    git push origin 1.0.0

Add a pipeline that publishes the component automatically when you create a tag:

.gitlab-ci.yml
stages:
- test
- release
# Test the component with itself (dogfooding)
test:component:
stage: test
trigger:
include:
- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
inputs:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
strategy: mirror
# Publish in the catalog through a GitLab release
create-release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:v0.24.0@sha256:3f52d526f48e8d10625c722fa6d6423ee82aadeae1b1aa91f07b62551d96dacf
script:
- echo "Publishing $CI_COMMIT_TAG to catalog"
release:
tag_name: $CI_COMMIT_TAG
description: "Release $CI_COMMIT_TAG"
rules:
- if: $CI_COMMIT_TAG =~ /^\d+\.\d+\.\d+$/

Be careful: release-cli has been deprecated since GitLab 18.0, with its removal planned for GitLab 20.0. The release: keyword remains the normal way, but it now requires the glab tool in the job's $PATH.

This example nevertheless keeps release-cli, pinned by digest, because the GitLab 17.0 release notes stated that release with release-cli was the only supported way to publish a CI/CD component. Switching to the glab image for this specific case has not been verified here: check it in the catalog documentation before migrating. For an ordinary pipeline (not a component), the glab route is described in GitLab CI/CD workflows.

Migrating an include template to a component

If you already have templates shared through include: project:, the migration is gradual:

  1. Create the component project with the templates/ structure

  2. Add spec:inputs to formalize the implicit parameters

    # Before (classic template): parameters through implicit variables
    .build:
    image: $BUILD_IMAGE
    script:
    - $BUILD_COMMAND
    # After (component): explicit, documented parameters
    spec:
    inputs:
    image:
    description: "Docker image for the build"
    build_command:
    default: "npm run build"
    ---
    build:
    image: $[[ inputs.image ]]
    script:
    - $[[ inputs.build_command ]]
  3. Publish version 1.0.0 in the catalog

  4. Migrate the consumers one by one:

    # Before
    include:
    - project: 'devops/ci-templates'
    file: '/templates/build.yml'
    ref: 'main'
    # After
    include:
    - component: $CI_SERVER_FQDN/devops/ci-components/build@1.0.0
    inputs:
    image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
  5. Keep the old template temporarily for the projects not yet migrated

Best practices

Naming and organization

The organization of a component is decided early, because renaming it after publication breaks the include paths of all the consumers. The guiding principle is splitting by responsibility: one template per task (build.yml, test.yml, lint.yml) rather than a monolithic file, which lets a project include only what it needs. Group these projects in a dedicated group to centralize discovery, and take care of the README, it remains the component's showcase in the catalog.

PracticeRecommendation
One project per domainci-components-nodejs, ci-components-docker, ci-components-terraform
One template per responsibilitybuild.yml, test.yml, lint.yml, not a monolith
Dedicated groupdevops/ci-components/ to centralize
Polished READMEIt is the showcase in the catalog

Semantic versioning

Since consumers pin a precise version, your choice of version number is a contract. The point that breaks the most pipelines is the move to a major version: as soon as you remove an input or change a default behavior, increment the first number, otherwise a simple tag update on a consumer's side will make its pipeline fail without warning. Conversely, a new optional input with a default value remains backward-compatible and only deserves a minor version.

ChangeVersionExample
Fix with no interface changePatch 1.0.1Fix a bug in the script
New optional inputMinor 1.1.0Add with_coverage: false
Removing an input, changing behaviorMajor 2.0.0Rename build_cmd to build_command

Security

A shared component runs its code in the pipeline of every project that includes it: a flaw in a component spreads to all its consumers. The most important rule here is the first one: no secret in the template itself, neither in clear text nor as a hard-coded variable, because the component's code is readable by everyone. Secrets stay in the CI/CD variables of the consuming project, which the job reads at runtime. Treat a widely distributed component with the same review rigor as a shared library.

  • Do not expose secrets in the templates, secrets must stay in the CI/CD variables of the consuming project
  • Validate the inputs: use types (number, boolean) to prevent injections
  • Code review: a component shared by 50 projects deserves the same rigor as a shared library

Troubleshooting

Component errors fall into two easily distinguished families. Either the component is not found or does not appear in the catalog, and the cause is almost always a wrong path or a missing release. Or the component is found but rejects your inputs, and you then need to refer to the component's page in the catalog to check the required inputs and their types. If in doubt about what a component really produces, the Pipeline editor tool with its Visualize view resolves the configuration and shows all the jobs.

SymptomLikely causeSolution
component not foundWrong path or the CI/CD Catalog project setting is not enabled on the projectCheck the full path <fqdn>/<namespace>/<project>/<template>@<tag> and that the catalog is enabled
input 'xxx' is requiredAn input without a default is not providedLook at the component's page in the catalog to see the required inputs
invalid input typeExpected type number but a string value was providedCheck the types declared in spec:inputs
The component does not show in the catalogNo GitLab release published (a tag alone is not enough)Create an X.Y.Z tag, check that the release job succeeded and the project permissions
Change not visible after an updatePinned versionIncrement the tag and update @version on the consumers
Do not know which jobs the component createsInsufficient documentationUse Pipeline editor > Visualize or CI Lint to see the resolved config

Key takeaways

  • The CI/CD Catalog is the entry point: look there for components before writing YAML, Explore > CI/CD Catalog
  • include:component replaces include: project: for cross-team sharing, a typed, versioned, documented interface
  • Use $CI_SERVER_FQDN instead of a hard-coded domain name to make your pipelines portable
  • Each component declares its interface through spec:inputs, the $[[ inputs.xxx ]] syntax is evaluated before the pipeline is created
  • Always pin a semantic version (@1.0.0), never @main
  • A component creates complete jobs in your pipeline, use the Pipeline editor to visualize the result
  • Classic include remains suitable for intra-team sharing of simple YAML files
  • The migration from include to components is gradual: both approaches coexist

Test your knowledge

Knowledge check

Check what you have learned with this interactive quiz

10 questions
5 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn