Skip to main content

components

GitLab CI/CD Components

Build reusable, versioned pipeline components for the CI/CD Catalog to share across 70+ projects.

Table of Contents

What are CI/CD Components

Overview

CI/CD Components are reusable, versioned units of pipeline configuration that can be shared across projects via the GitLab CI/CD Catalog.

Key features:

  • Versioned: Use semantic versioning (v1.0.0, v1.1.0, etc.)
  • Cataloged: Discoverable in GitLab's CI/CD Catalog
  • Parameterized: Accept inputs for dynamic behavior
  • Composable: Multiple components in a single repository
  • Maintained: Single source of truth across projects

Source: GitLab CI/CD Components Documentation

Why Use Components?

Before Components (traditional templates):

# Every project copies the same config include: - template: Security/SAST.gitlab-ci.yml # Or remote includes with no version control include: - remote: 'https://example.com/ci-templates/deploy.yml'

Problems:

  • No versioning
  • Hard to discover
  • Difficult to maintain across 70+ projects
  • Breaking changes affect all projects immediately

With Components:

include: - component: $CI_SERVER_FQDN/my-group/components/deploy@v1.2.0 inputs: environment: production replicas: 3

Advantages:

  • Versioned (v1.2.0 = stable, predictable)
  • Discoverable in CI/CD Catalog
  • Parameterized (inputs)
  • Easy to update (bump version)
  • Safe rollout (test new version before migrating all projects)

Source: Building Reusable CI/CD Templates

Components vs Templates

Comparison

FeatureComponentsTemplates (Deprecated)
VersioningYes (semantic versioning)No
CatalogListed in CI/CD CatalogNot listed
InputsDynamic inputsStatic or env vars
DiscoverabilityHigh (searchable catalog)Low (documentation only)
MaintenanceCentralizedScattered
MigrationGradual (by version)Immediate (breaking)

Migration Path

GitLab is no longer accepting new CI/CD templates. Use components instead.

Old way (deprecated):

include: - template: Jobs/Deploy.gitlab-ci.yml

New way (recommended):

include: - component: $CI_SERVER_FQDN/gitlab-org/components/deploy@v2.0.0

Source: CI/CD Component Examples

Creating Components

Component Project Structure

Repository layout:

my-components/
 README.md
 templates/
    build.yml
    test.yml
    deploy.yml
 .gitlab-ci.yml  # For testing components

Example: templates/build.yml

spec: inputs: image: default: node:18 cache_key: default: npm-cache --- build: image: $[[ inputs.image ]] cache: key: $[[ inputs.cache_key ]] paths: - node_modules/ script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 day

Key sections:

  1. spec block: Defines inputs with defaults
  2. Job definition: Uses $[[ inputs.variable ]] syntax

Defining Inputs

Input types:

  • String: Text values
  • Number: Numeric values
  • Boolean: true/false
  • Array: List of values
  • Object: Nested key-value pairs

Example with multiple input types:

spec: inputs: environment: type: string default: staging description: "Deployment environment" replicas: type: number default: 2 description: "Number of replicas" enable_monitoring: type: boolean default: true description: "Enable monitoring" regions: type: array default: [us-east-1, us-west-2] description: "Deployment regions" labels: type: object default: app: myapp team: platform description: "Kubernetes labels" --- deploy: script: - echo "Deploying to $[[ inputs.environment ]]" - echo "Replicas: $[[ inputs.replicas ]]" - | if [[ "$[[ inputs.enable_monitoring ]]" == "true" ]]; then echo "Monitoring enabled" fi

Source: GitLab CI/CD Components

Multiple Components in One Repository

Repository structure:

platform-components/
 README.md
 templates/
    node-build/
       template.yml
    docker-build/
       template.yml
    security-scan/
       template.yml
    deploy-k8s/
        template.yml
 .gitlab-ci.yml

Each subdirectory is a separate component that can be versioned together.

Usage:

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 - component: $CI_SERVER_FQDN/platform/components/deploy-k8s@v1.0.0

Component Inputs

Required vs Optional Inputs

Required inputs (no default):

spec: inputs: api_token: description: "API token for deployment" # No default = required

Usage (must provide):

include: - component: example.com/my-group/deploy@v1.0.0 inputs: api_token: $DEPLOY_TOKEN # Must be provided

Optional inputs (with default):

spec: inputs: environment: default: staging description: "Deployment environment"

Usage (can override or use default):

include: - component: example.com/my-group/deploy@v1.0.0 inputs: environment: production # Override default

Input Validation

Add descriptions to guide users:

spec: inputs: environment: default: staging description: | Target environment for deployment. Valid values: development, staging, production Default: staging

Document in README:

# Deploy Component ## Inputs | Input | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `environment` | string | No | `staging` | Deployment environment | | `api_token` | string | Yes | - | API token for authentication | | `replicas` | number | No | `2` | Number of replicas |

Using GitLab CI Variables as Inputs

Pass predefined variables:

include: - component: $CI_SERVER_FQDN/platform/components/deploy@v1.0.0 inputs: environment: $CI_ENVIRONMENT_NAME commit_sha: $CI_COMMIT_SHORT_SHA project: $CI_PROJECT_NAME

Component usage:

spec: inputs: environment: commit_sha: project: --- deploy: script: - echo "Deploying $[[ inputs.project ]] ($[[ inputs.commit_sha ]]) to $[[ inputs.environment ]]"

Versioning Components

Semantic Versioning

Format: vMAJOR.MINOR.PATCH

  • MAJOR: Breaking changes (require updates in consuming projects)
  • MINOR: New features (backward compatible)
  • PATCH: Bug fixes (backward compatible)

Examples:

  • v1.0.0: Initial release
  • v1.1.0: Added new optional input (backward compatible)
  • v1.1.1: Fixed bug (backward compatible)
  • v2.0.0: Changed required inputs (breaking change)

Creating Releases

1. Tag the repository:

git tag v1.0.0 git push origin v1.0.0

2. Create release (GitLab UI):

  • Navigate to: Project Deployments Releases New release
  • Tag: v1.0.0
  • Release title: v1.0.0
  • Description: Changelog

3. Component is now available at this version:

include: - component: $CI_SERVER_FQDN/my-group/component@v1.0.0

Version Ranges

Exact version (recommended for production):

include: - component: example.com/my-group/deploy@v1.2.3

Major version (auto-update to latest minor/patch):

include: - component: example.com/my-group/deploy@v1

Minor version (auto-update to latest patch):

include: - component: example.com/my-group/deploy@v1.2

Latest (not recommended - unpredictable):

include: - component: example.com/my-group/deploy@latest

Best practice: Use exact versions for production, major/minor for development.

Managing Breaking Changes

Scenario: v1.x.x v2.0.0 (breaking change)

Strategy:

  1. Release v2.0.0 with breaking changes
  2. Keep v1.x.x available for existing users
  3. Document migration guide in v2.0.0 release notes
  4. Gradually migrate projects from v1 v2

Example migration:

v1.0.0 (old):

spec: inputs: env: # Old name default: staging

v2.0.0 (breaking):

spec: inputs: environment: # New name (breaking change) default: staging

Migration guide (in README):

## Migrating from v1.x to v2.0 **Breaking Changes:** - Input `env` renamed to `environment` **Before (v1.x):** ```yaml include: - component: .../deploy@v1 inputs: env: production

After (v2.0):

include: - component: .../deploy@v2 inputs: environment: production

**Source**: [Scaling GitLab CI/CD with Reusable Templates](https://dev.to/yoriiis/scaling-gitlab-cicd-with-reusable-templates-5ho0)

## Publishing to CI/CD Catalog

### Requirements

**To publish components to the catalog**:
1. Repository must be a **component project** (contains `templates/` directory)
2. Must have at least one **release** (tagged version)
3. Must include **README.md** with documentation
4. Repository must be **public** or **internal** (not private)

### Catalog Metadata

**Add catalog metadata** to repository description:

**Repository  Settings  General  Project description**:

Reusable CI/CD components for Node.js builds, testing, and deployment


**README.md** (required):
```markdown
# Platform CI/CD Components

Reusable pipeline components for the Agent Platform.

## Components

### node-build
Builds Node.js applications with caching.

**Usage:**
```yaml
include:
  - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0
    inputs:
      node_version: "18"

Inputs:

InputTypeDefaultDescription
node_versionstring18Node.js version

deploy-k8s

Deploys to Kubernetes.

Usage:

include: - component: $CI_SERVER_FQDN/platform/components/deploy-k8s@v1.0.0 inputs: environment: production replicas: 3

### Publishing Process

**1. Create component repository**:
```bash
mkdir platform-components
cd platform-components
git init
mkdir -p templates/node-build

2. Add component:

templates/node-build/template.yml:

spec: inputs: node_version: default: "18" --- build: image: node:$[[ inputs.node_version ]] script: - npm ci - npm run build

3. Add README:

cat > README.md <<'EOF' # Platform Components Reusable CI/CD components. ## node-build Builds Node.js applications. EOF

4. Commit and push:

git add . git commit -m "feat: add node-build component" git remote add origin https://gitlab.com/my-group/platform-components.git git push -u origin main

5. Create release:

git tag v1.0.0 git push origin v1.0.0

6. Verify in catalog:

  • Navigate to: Group Build CI/CD Catalog
  • Search for your component

Source: GitLab CI/CD Catalog

Using Components

Basic Usage

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0

Explanation:

  • $CI_SERVER_FQDN: GitLab instance domain (e.g., gitlab.com)
  • /platform/components: Group and project path
  • /node-build: Component name (subdirectory in templates/)
  • @v1.0.0: Version

With Inputs

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 inputs: node_version: "20" cache_key: custom-cache

Multiple Components

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 inputs: node_version: "18" - component: $CI_SERVER_FQDN/platform/components/security-scan@v2.1.0 inputs: severity: high - component: $CI_SERVER_FQDN/platform/components/deploy-k8s@v1.5.0 inputs: environment: production replicas: 5

Combining with Local Jobs

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 # Local job that extends component custom-test: stage: test needs: [build] # build from component script: - npm run test:custom

Overriding Component Behavior

Component defines defaults:

# templates/node-build/template.yml build: image: node:18 cache: key: npm-cache paths: - node_modules/ script: - npm ci - npm run build

Project can override:

include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 # Override specific fields build: cache: key: custom-cache-$CI_COMMIT_REF_SLUG # Override cache key

Best Practices

1. Design for Reusability

Do: Make components generic

spec: inputs: build_command: default: "npm run build" --- build: script: - $[[ inputs.build_command ]]

Don't: Hard-code project-specific logic

build: script: - npm run build:my-specific-project # Not reusable

2. Provide Sensible Defaults

Every input should have a default (unless truly required):

spec: inputs: node_version: default: "18" # Most projects use Node 18 cache_enabled: default: true # Caching should be opt-out, not opt-in

3. Document Thoroughly

README should include:

  • Component purpose
  • All inputs (with types, defaults, descriptions)
  • Usage examples
  • Version compatibility
  • Migration guides for breaking changes

Example:

## node-build Builds Node.js applications with dependency caching. ### Inputs | Input | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `node_version` | string | No | `18` | Node.js version | | `cache_enabled` | boolean | No | `true` | Enable dependency caching | | `build_command` | string | No | `npm run build` | Build command | ### Example ```yaml include: - component: $CI_SERVER_FQDN/platform/components/node-build@v1.0.0 inputs: node_version: "20" build_command: "npm run build:prod"

### 4. Version Strategically

**Versioning guidelines**:
- **Patch (v1.0.X)**: Bug fixes, documentation updates
- **Minor (v1.X.0)**: New optional inputs, new features (backward compatible)
- **Major (vX.0.0)**: Breaking changes (renamed inputs, removed features)

**Example**:
- v1.0.0: Initial release
- v1.1.0: Add `cache_enabled` input (optional, default: true)  backward compatible
- v1.1.1: Fix cache path bug  backward compatible
- v2.0.0: Rename `node_version`  `node`  **breaking change**

### 5. Test Components Before Publishing

**Create test pipeline** in component repository:

`.gitlab-ci.yml`:
```yaml
include:
  - local: templates/node-build/template.yml

# Test with different inputs
test-node-18:
  stage: test
  before_script:
    - echo "Testing with Node 18"
  variables:
    inputs.node_version: "18"

test-node-20:
  stage: test
  before_script:
    - echo "Testing with Node 20"
  variables:
    inputs.node_version: "20"

Run tests before tagging:

git push # Trigger pipeline # Verify all tests pass git tag v1.0.0 git push origin v1.0.0

6. Maintain Backward Compatibility

Avoid breaking changes when possible:

Bad (breaking):

# v1.0.0 spec: inputs: env: ... # v2.0.0 spec: inputs: environment: ... # Renamed - breaks v1 users

Good (backward compatible):

# v1.1.0 - Add new input, keep old one spec: inputs: env: default: staging deprecated: true # Mark as deprecated environment: default: $[[ inputs.env ]] # Use old value if new not provided

7. Use Namespaces

Organize components by domain:

platform-components/
 templates/
    builds/
       node/
       go/
       docker/
    tests/
       unit/
       integration/
       e2e/
    deploys/
        k8s/
        lambda/
        ecs/

Usage:

include: - component: .../platform-components/builds/node@v1.0.0 - component: .../platform-components/tests/unit@v1.0.0 - component: .../platform-components/deploys/k8s@v1.0.0

8. Monitor Component Usage

Track adoption:

  • Number of projects using each component
  • Version distribution (how many still on v1 vs v2?)
  • Common input configurations
  • Error rates per component

Use GitLab API to query component usage across projects.

9. Deprecation Strategy

When deprecating a component:

  1. Announce deprecation in release notes
  2. Mark as deprecated in README
  3. Keep available for 6-12 months
  4. Provide migration guide to replacement
  5. Archive repository (don't delete - existing refs may break)

Example deprecation notice:

# DEPRECATED This component is deprecated and will be archived on 2026-12-31. **Migration:** Use [new-component](../new-component/) instead. See [migration guide](./MIGRATION.md) for details.

Advanced Patterns

Conditional Component Logic

spec: inputs: enable_cache: type: boolean default: true --- build: script: - npm ci - npm run build cache: key: npm-cache paths: - node_modules/ rules: - if: '$[[ inputs.enable_cache ]] == "true"' cache: key: npm-cache paths: - node_modules/ - when: always

Component Composition

Base component:

# templates/base/template.yml .base: before_script: - echo "Setup" after_script: - echo "Cleanup"

Specialized component (extends base):

# templates/node-build/template.yml include: - component: $CI_SERVER_FQDN/platform/components/base@v1.0.0 build: extends: .base script: - npm run build

Dynamic Job Names

spec: inputs: environment: default: staging --- deploy-$[[ inputs.environment ]]: script: - echo "Deploying to $[[ inputs.environment ]]"

Result: Job name becomes deploy-staging or deploy-production based on input.

Additional Resources


Last Updated: 2026-01-08 Priority: HIGH - Essential for managing 70+ projects efficiently