Chapter 12Lesson 03~150 minutes

YAML Reuse with include, extends, Anchors, !reference, Hidden Jobs, and Template Architecture: Configuration, Design Choices, and Tradeoffs

Reuse creates leverage and blast radius at the same time. This lesson turns local/project/remote/template includes, extends versus anchors, depth, version pinning, and caller override contracts into explicit design choices tied to trust, auditability, portability, and rollback.

YAML reuseincludeextends!referenceTemplate trust

Learning objectives

  • Choose local, project, remote, template, or component-style reuse based on ownership, trust, portability, and versioning.
  • Compare extends with anchors and !reference based on merge semantics, cross-file capability, and readability.
  • Design caller override contracts that avoid accidental array replacement and undocumented inheritance.
  • Prefer immutable or protected references and integrity checks for executable configuration dependencies.
  • Use a worked decision matrix covering maintainability, least privilege, rollback, tier/offering, and evidence.

1. Reuse architecture is an ownership and trust decision

The shortest YAML is not necessarily the best reusable architecture. Every abstraction adds an owner, compatibility promise, failure mode, and upgrade path. Decide first who owns the reusable code, who may change it, who consumes it, and how a consumer can pin/roll back it.

2. Local vs project vs remote vs template

Choice Best fit Trust/rollback concern
include:local One repository; reuse should follow the same commit Simple and atomic with source; no independent release cadence
include:project Shared internal library on same GitLab instance Requires access; pin full SHA or protected version tag; change management belongs to template project
include:remote Public externally hosted reviewed configuration No authenticated fetch; use integrity and strong upstream ownership
include:template GitLab-provided templates Version/deprecation tied to GitLab release; inspect template intent
component Versioned reusable building block with explicit inputs Chapter 13; stronger interface semantics but still executable dependency code

3. extends vs anchors vs !reference

Mechanism Cross file? Merge/select model Use when
YAML anchor No YAML alias/map merge within one file Small local structural duplication
extends Yes Reverse deep merge by keys; arrays replace Reusable job templates and readable inheritance
!reference Yes Select exact keyword/nested configuration path Reuse a particular rules/script subsection across files

Prefer readability over clever nesting. A small amount of obvious duplication can be safer than a four-layer inheritance chain whose effective behavior requires specialist knowledge.

4. Design around array replacement explicitly

Scripts, tags, services, and rules are often arrays. If a child defines the same array key, do not assume it appends to the parent. Make replacement intentional or select reusable arrays with !reference/anchors where appropriate.

.base:
  script:
    - echo "parent"

child:
  extends: .base
  script:
    - echo "child"  # effective script is only this array

5. Deep inheritance is a maintainability cost

GitLab can resolve up to eleven levels of extends inheritance, but the documentation recommends avoiding more than three. The limit is not a design target. Each layer increases the number of files/definitions a reviewer must mentally compose and makes array replacement or nulling harder to see.

Use one or two semantic layers—such as .base_test and .python_test—then prefer explicit job configuration for the final differences.

6. Moving refs trade convenience for reproducibility

A branch such as main makes upgrades automatic but also changes consumers without a consumer-side review. A protected release tag narrows who can move the version name; a full commit SHA is the strongest immutable identity. For critical shared pipeline logic, pin the immutable revision and upgrade through a normal review.

Reference Change behavior Rollback
main / branch Moves whenever branch advances Harder to reproduce old pipeline unless historical SHA recorded
protected version tag Intended release boundary; still governance-sensitive Move consumer back to prior tag if tags immutable/protected
full SHA Immutable commit identity Change one reviewed SHA back to prior known-good SHA

7. Remote include needs integrity and upstream review

include:remote cannot authenticate to the URL. That makes it unsuitable for private template retrieval and raises supply-chain questions. Current GitLab supports include:integrity for SHA-256 verification of remote content. Integrity proves content bytes match the expected digest; it does not tell you that those bytes are good. You still need an upstream review/release process.

8. Define what callers may override

A template should document required variables/inputs, default image, stages, rules, artifacts, side effects, and which keys consumers may override. Without a contract, consumers depend on internal details and template maintainers cannot change implementation safely.

For simple YAML reuse, comments and documentation can express the contract. Chapter 13 introduces CI/CD components and typed inputs, which formalize this idea further.

9. Treat included CI as code with runner-level authority

An included template can add scripts, images, services, artifacts, network calls, or deployment jobs. If a consuming project has protected variables or privileged runners, a malicious template change can exercise those privileges. Review reusable CI like application code and scope the runner/secret reach of consumers independently.

Least privilege: pin dependencies, restrict who can change shared templates, keep privileged jobs on protected refs/runners, and never assume “central template” means “trusted forever.”

10. Worked scenario: twenty services need the same test baseline

The organization owns one GitLab group and wants a standard test image, lint job, and report artifact. Today the baseline changes monthly and must be reviewable per service.

Option Fit Decision
Copy YAML into each repo Low central blast radius, high drift Reject as primary long-term pattern
Remote URL on a branch Easy but weak ownership/pinning Reject for private internal baseline
Project include pinned to full SHA Good current fit; explicit shared owner and immutable consumer ref Choose now
CI/CD component with version + inputs Best interface for broader organization-wide reuse Migrate/extend in Chapter 13

Evidence: each service records the template project path, full SHA, expanded job, source SHA, and resulting report artifact. Upgrades happen by changing the pinned SHA in a reviewed merge request.

11. Tier/offering and portability

The core include/extends/anchor/reference mechanisms used here are available across GitLab offerings. Specific templates/components/inputs or newer include subkeys can be version-sensitive. A portable template should document its minimum GitLab version and avoid relying silently on administrator settings such as increased include limits.

Knowledge check

Why is maximum inheritance depth not a target architecture?

When is include:local preferable to include:project?

What does remote integrity prove and not prove?

Why can a shared template be a privilege escalation path?

What is the cleanest rollback for a pinned project include?

Next lesson

Diagnostics, failure modes, security, and performance

Diagnose moving includes, access/integrity failures, array replacement, over-deep inheritance, and shared-template trust problems.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-11. CI reuse and include behavior evolves across GitLab releases, especially include subkeys, integrity/caching, inputs/components, and template deprecations. Re-check the deployed GitLab version before relying on newer include features on Self-Managed or Dedicated installations.

Current behavior used by this chapter: includes are resolved first and the root configuration is merged afterward; all include resolution has a 30-second time limit and the default nested-include limit is 150. A project include can pin a full 40-character commit SHA. Remote includes are public HTTP(S) GETs without authentication and can use integrity with a base64 SHA-256. YAML anchors cannot cross files. extends reverse-deep-merges hashes but array values are replaced, not concatenated. GitLab supports up to eleven inheritance levels but recommends avoiding deep inheritance; !reference can select configuration from included files and has parsing constraints with CI/CD inputs.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.