Includes, Templates, CI/CD Components, Component Catalog, and Reusable Pipeline Architecture: Concepts, Architecture, and Mental Model
Build a mental model for GitLab reusable CI/CD configuration: include sources, merge order, typed component inputs, catalog lifecycle, immutable identity, and supply-chain trust.
Learning objectives
- Distinguish local/project/remote/template/component include sources and their provenance boundaries.
- Explain recursive include merging and why the final merged YAML is authoritative.
- Separate typed inputs from runtime variables/secrets.
- Explain component version identity, Catalog lifecycle, and supply-chain trust.
- Inspect reusable configuration read-only before changing it.
include, CI/CD inputs, CI/CD components, the CI/CD
Catalog, and CI Lint are available on Free/Premium/Ultimate across
GitLab.com, Self-Managed, and Dedicated. Component references must
resolve on the same GitLab instance as the consuming project. The
mandatory labs require only a disposable Free project and local
configuration. Catalog publication is optional because publishing
requires a catalog-enabled component project, appropriate project
roles, a semantic-version release, and repository metadata. The
current glab repo publish catalog command is experimental
and therefore is not a mandatory production path.
1. The practical problem: copy-pasted YAML becomes an ungoverned dependency graph
Chapters 10–19 built increasingly capable pipelines. The next scaling problem is not syntax; it is reuse. Teams copy the same test, scan, build, or deployment fragments into many repositories. Those copies drift, fixes propagate slowly, and a one-line change in a shared source can unexpectedly alter hundreds of consumers.
GitLab provides several reuse layers. The key is to choose the smallest layer that matches the ownership boundary, then treat any remote reusable configuration as supply-chain code.
2. Mental model: source → fetch → validate → merge → instantiate → execute
flowchart TD R[Repository / \n.gitlab-ci.yml] --> I[include resolver] L[local include same commit] --> I P[project include project + ref + file] --> I X[remote include HTTPS + optional integrity] --> I T[GitLab template] --> I C[CI/CD component version + typed inputs] --> I I --> M[Merged CI configuration] M --> V[CI Lint / pipeline creation] V --> J[Jobs] J --> Q[Runners + runtime credentials]
GitLab resolves included configuration before pipeline jobs exist. Inputs are interpolated during configuration creation, then the resulting YAML is merged. The final merged configuration—not any one source file—is what defines jobs. Once jobs run, the runner and runtime credentials form a separate trust boundary.
3. Five include sources, five different provenance stories
| Source | Identity | Trust/availability characteristic |
|---|---|---|
include:local |
Path in the same repository and same commit | Strongest coupling to the consumer commit; excellent for repository-local decomposition. |
include:project |
GitLab project + file + optional ref |
Cross-project reuse on the same instance. Pin a full commit SHA when reproducibility matters. |
include:remote |
HTTPS URL |
External network and content owner become dependencies.
Prefer immutable URL/content plus integrity.
|
include:template |
GitLab-provided template name | GitLab-maintained template surface; inspect expanded configuration and version-specific behavior. |
include:component |
FQDN/project/component@version |
Versioned reusable unit with spec:inputs;
component must be on the same GitLab instance.
|
4. Merge order is executable behavior, not formatting
GitLab recursively resolves nested includes first. Included files
are merged in the order listed; when included configuration
overlaps, later included configuration takes precedence. After the
include set is assembled, the main .gitlab-ci.yml is
merged and can override overlapping values. This means reading only
the shared template is insufficient when diagnosing a job.
include:
- local: .gitlab/ci/base.yml
- local: .gitlab/ci/team.yml
# The main file is merged after both includes.
test:
variables:
OWNER: application-team
Use CI Lint with merged YAML when the effective result matters. Do not infer inheritance from file layout.
5. Inputs are compile-time contracts; variables are runtime state
A reusable file can declare spec:inputs with types,
defaults, options, regex validation, and descriptions. Input
interpolation occurs when GitLab fetches configuration, before the
configuration is merged. A component or include consumer must pass
required values explicitly.
spec:
inputs:
stage:
default: test
mode:
options: [quick, full]
default: quick
retries:
type: number
default: 1
---
shared-test:
stage: $[[ inputs.stage ]]
script:
- printf 'mode=%s retries=%s\n' '$[[ inputs.mode ]]' '$[[ inputs.retries ]]'
Inputs are appropriate for configuration contracts. Secrets remain runtime secret material and should not be embedded in reusable YAML or passed casually as inputs.
6. A CI/CD component is a versioned configuration product
A component packages one reusable CI/CD unit under a component
project. Component templates live under templates/;
consumers reference a component as
host/project/component@version. Components can use
typed inputs and can be published to the CI/CD Catalog for
discovery.
include:
- component: $CI_SERVER_FQDN/platform/ci-components/quality@1.4.2
inputs:
stage: test
strict: true
Components should be narrow, composable, documented, tested, and conservative about credentials. GitLab explicitly recommends auditing third-party component source and using minimum-privilege credentials.
7. Version identity: SHA, release tag, branch, partial SemVer, or ~latest
| Reference | Reproducibility | Use |
|---|---|---|
| Full commit SHA | Highest | Strong pin for reviewed exact content; especially useful for ordinary project includes/components. |
Full semantic release such as 1.4.2 |
High | Human-readable immutable release contract; recommended catalog consumption pattern. |
Partial version such as 1.4 |
Floating within compatible line | Catalog components only; accepts newer matching published patch releases. |
~latest |
Low | Follows latest published catalog release; unsuitable where unexpected changes are unacceptable. |
Branch such as main |
Low | Useful for development/testing, not deterministic production consumption. |
A release tag is only as trustworthy as the repository’s tag/release governance. For maximum immutability, a reviewed full commit SHA is strongest; for managed component products, a semantic release tag gives a clear compatibility contract.
8. Remote includes need content integrity, not just HTTPS
include:remote crosses outside the GitLab project trust
boundary. Current GitLab supports integrity, a
base64-encoded SHA-256 digest. If the fetched content no longer
matches, GitLab rejects it instead of silently compiling changed
configuration.
include:
- remote: 'https://example.invalid/ci/verified.yml'
integrity: 'sha256-BASE64_SHA256_OF_REVIEWED_CONTENT'
The lab uses example.invalid only as documentation. Do
not turn a live third-party URL into a mandatory dependency.
9. Catalog publication is a lifecycle, not an upload button
The Catalog is Free across offerings. A project must first be marked
as a Catalog project, then a semantic-version release publishes the
component version. Current prerequisites include a project
description, root README.md, component configuration
under templates/, and creation of the release from a CI
job using the release keyword. GitLab requires Owner to
enable Catalog-project status and Maintainer or Owner for
publication/release work.
Publication should therefore follow tests, review, release notes, compatibility classification, and consumer migration planning.
10. Read-only inspection before editing reuse boundaries
PROJECT_ID="12345678"
# Inspect repository configuration at the current ref.
git show HEAD:.gitlab-ci.yml
# Ask GitLab for expanded/merged configuration using CI Lint.
jq --null-input --arg yaml "$(cat .gitlab-ci.yml)" '{content:$yaml}' \
| glab api --method POST "projects/$PROJECT_ID/ci/lint?include_merged_yaml=true" \
--header "Content-Type: application/json" \
--input - \
--jq '{valid,errors,warnings,merged_yaml}'
The exact authentication mechanics of glab api stay
outside logs. Never print tokens to prove that linting works.
11. DevOps connection: reusable YAML is executable supply-chain code
A component can select images, invoke scripts, access job tokens, receive project variables, and run on privileged infrastructure. Therefore component review belongs in the same threat model as application dependencies: identify publisher, immutable revision, transitive dependencies, expected permissions, release process, and rollback path.
Knowledge check
Why is include:local usually easier to reproduce
than include:project on a branch?
The local file is resolved from the same repository commit as the consumer. A project include that points at a moving branch can change without a consumer commit.
Which configuration wins when an included job and the main
.gitlab-ci.yml define overlapping values?
After includes are resolved and merged, the main configuration is merged last and can override overlapping included values.
Are typed inputs a good place to pass secrets?
No. Inputs are a configuration contract evaluated during pipeline creation. Secret material should use appropriate runtime secret mechanisms and least-privilege scope.
What does a full component commit SHA protect you from?
It prevents the referenced component revision from silently moving. It does not by itself prove the code is safe; review and runner/credential controls are still required.
Can a GitLab.com component be referenced directly by
include:component from an unrelated Self-Managed
instance?
No. Component references resolve on the same GitLab instance. Mirroring/publication is a separate workflow, with additional tier/administrative considerations.
Summary
Includes assemble reusable configuration; components add a versioned typed contract and Catalog lifecycle. Effective behavior comes from the final merged YAML. Prefer same-commit local reuse when scope is local, immutable project/component references when crossing ownership boundaries, and content integrity for remote includes. Treat every reusable CI dependency as executable supply-chain code.
Official references
Primary sources used for the current GitLab 19.3 behavior taught in this lesson:
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.