
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
includeand 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.
- Writing a .gitlab-ci.yml file
- GitLab CI/CD templates (include, extends)
- extends and YAML anchors in GitLab CI/CD
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.
| Criterion | include | Components |
|---|---|---|
| Complexity | Simple, include a YAML file | More structured, dedicated project |
| Parameters | Environment variables (untyped) | Typed spec:inputs with defaults |
| Versioning | Optional ref: | Git tags required for the catalog |
| Documentation | Separate README (if you think of it) | Built into the catalog |
| Discoverability | You need to know the path | Search in the catalog |
| Validation | None, the YAML is included as is | Inputs validated at evaluation |
| Use case | Internal team sharing, small projects | Cross-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:
| Component | Usage | Reference |
|---|---|---|
components/sast | Static application security testing | gitlab.com/components/sast@<version> |
components/secret-detection | Secret detection | gitlab.com/components/secret-detection@<version> |
components/code-quality | Code quality | gitlab.com/components/code-quality@<version> |
components/container-scanning | Docker image scanning | gitlab.com/components/container-scanning@<version> |
Using a component
include:component syntax
Include a component in your .gitlab-ci.yml with the include:component directive:
include: - component: $CI_SERVER_FQDN/components/sast@7.3.2 inputs: stage: test
stages: - test - build
build: stage: build script: npm run buildThis 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>| Part | Meaning | Example |
|---|---|---|
<instance> | Your GitLab instance | gitlab.com or gitlab.example.com |
<namespace>/<project> | Group and project of the component | components/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.
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 buildWhat 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):
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: trueHere, 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 valueinclude: - component: $CI_SERVER_FQDN/devops/ci-components-nodejs/build@1.0.0 inputs: image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5To 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:
- Read the documentation of the component in the catalog
- Use the pipeline editor: go to Build > Pipeline editor and click "Visualize". GitLab resolves the components and shows all the jobs
- Look at CI Lint: in Build > Pipeline editor > CI Lint, paste your
.gitlab-ci.ymland 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 itselfEach 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:
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 daysThe --- 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.
| Type | Description | Example value |
|---|---|---|
string | Text value (default type) | "node:20" |
number | Numeric value | 30 |
boolean | true / false | true |
array | List of values | ["docker", "linux"] |
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.
-
Go to Settings > General > Visibility, project features, permissions
-
Enable CI/CD Catalog project (this setting has had this name since GitLab 16.10; it was previously called "CI/CD Catalog resource")
-
Add a README.md describing your components (this is the page displayed in the catalog)
-
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
Recommended release pipeline
Add a pipeline that publishes the component automatically when you create a tag:
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 releasecreate-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:
-
Create the component project with the
templates/structure -
Add
spec:inputsto formalize the implicit parameters# Before (classic template): parameters through implicit variables.build:image: $BUILD_IMAGEscript:- $BUILD_COMMAND# After (component): explicit, documented parametersspec:inputs:image:description: "Docker image for the build"build_command:default: "npm run build"---build:image: $[[ inputs.image ]]script:- $[[ inputs.build_command ]] -
Publish version 1.0.0 in the catalog
-
Migrate the consumers one by one:
# Beforeinclude:- project: 'devops/ci-templates'file: '/templates/build.yml'ref: 'main'# Afterinclude:- component: $CI_SERVER_FQDN/devops/ci-components/build@1.0.0inputs:image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5 -
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.
| Practice | Recommendation |
|---|---|
| One project per domain | ci-components-nodejs, ci-components-docker, ci-components-terraform |
| One template per responsibility | build.yml, test.yml, lint.yml, not a monolith |
| Dedicated group | devops/ci-components/ to centralize |
| Polished README | It 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.
| Change | Version | Example |
|---|---|---|
| Fix with no interface change | Patch 1.0.1 | Fix a bug in the script |
| New optional input | Minor 1.1.0 | Add with_coverage: false |
| Removing an input, changing behavior | Major 2.0.0 | Rename 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.
| Symptom | Likely cause | Solution |
|---|---|---|
component not found | Wrong path or the CI/CD Catalog project setting is not enabled on the project | Check the full path <fqdn>/<namespace>/<project>/<template>@<tag> and that the catalog is enabled |
input 'xxx' is required | An input without a default is not provided | Look at the component's page in the catalog to see the required inputs |
invalid input type | Expected type number but a string value was provided | Check the types declared in spec:inputs |
| The component does not show in the catalog | No 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 update | Pinned version | Increment the tag and update @version on the consumers |
| Do not know which jobs the component creates | Insufficient documentation | Use 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:componentreplacesinclude: project:for cross-team sharing, a typed, versioned, documented interface- Use
$CI_SERVER_FQDNinstead 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
includeremains 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
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
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: DAG and parallelism in GitLab CI/CD (needs)
A new full quiz with random questions
Work again only on the questions you missed