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
- Components vs Templates
- Creating Components
- Component Inputs
- Versioning Components
- Publishing to CI/CD Catalog
- Using Components
- Best Practices
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
| Feature | Components | Templates (Deprecated) |
|---|---|---|
| Versioning | Yes (semantic versioning) | No |
| Catalog | Listed in CI/CD Catalog | Not listed |
| Inputs | Dynamic inputs | Static or env vars |
| Discoverability | High (searchable catalog) | Low (documentation only) |
| Maintenance | Centralized | Scattered |
| Migration | Gradual (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:
specblock: Defines inputs with defaults- 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 releasev1.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:
- Release v2.0.0 with breaking changes
- Keep v1.x.x available for existing users
- Document migration guide in v2.0.0 release notes
- 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:
| Input | Type | Default | Description |
|---|---|---|---|
node_version | string | 18 | Node.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 intemplates/)@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:
- Announce deprecation in release notes
- Mark as deprecated in README
- Keep available for 6-12 months
- Provide migration guide to replacement
- 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
- Multi-Project Management - Managing 70+ projects
- Pipeline Patterns - Common CI/CD patterns
- Cost Optimization - Reduce CI minutes
- GitLab CI/CD Components Docs
- CI/CD Catalog
- Using GitLab's CI/CD Catalog
Last Updated: 2026-01-08 Priority: HIGH - Essential for managing 70+ projects efficiently