local, project, remote und template im Vergleich
Wer in mehr als einem Repository eine .gitlab-ci.yml pflegt, kennt das Problem: Build-, Test- und Deploy-Jobs werden immer wieder kopiert und driften mit der Zeit auseinander. Das include-Keyword loest genau dieses Problem, indem es Pipeline-Definitionen aus anderen Dateien, anderen Projekten oder sogar externen URLs zur Laufzeit zusammenfuehrt. Dieser Artikel zeigt alle vier include-Varianten im Detail und wie daraus eine zentrale CI-Template-Bibliothek fuer die gesamte Organisation wird.
Inhaltsverzeichnis
- 1. Warum include mehr ist als Kopieren und Einfuegen
- 2. include:local: Eine grosse Pipeline in mehrere Dateien aufteilen
- 3. include:project: Pipeline-Bausteine aus einem anderen Repository beziehen
- 4. include:remote: Externe URLs und ihre Grenzen
- 5. include:template: GitLab-eigene Standardvorlagen nutzen
- 6. Aufbau einer zentralen CI-Template-Bibliothek
- 7. Versionierung: Warum ref niemals main sein sollte
- 8. Parametrisierung mit spec:inputs und Kombination mit rules
- 9. Best Practices, Fallstricke und Vergleich der vier Varianten
- 10. Zusammenfassung
- 11. FAQ
1. Warum include mehr ist als Kopieren und Einfuegen
In den meisten Teams beginnt die GitLab-CI-Pipeline als eine einzelne .gitlab-ci.yml Datei, die mit dem Projekt waechst. Sobald ein zweites oder drittes Repository dazukommt, entsteht schnell die Versuchung, die funktionierende Pipeline einfach zu kopieren und die Projektnamen anzupassen. Das funktioniert kurzfristig, fuehrt aber mittelfristig zu einem Wartungsalbtraum: Eine Aenderung an der Docker-Registry-URL, ein neuer Cache-Key oder ein zusaetzlicher Security-Scan muss dann in zehn, zwanzig oder hundert Repositories einzeln nachgezogen werden.
include loest dieses Problem, indem es GitLab CI erlaubt, Job-Definitionen aus mehreren Quellen zu einer einzigen effektiven Pipeline zusammenzufuehren, bevor sie ausgefuehrt wird. Die lokale .gitlab-ci.yml wird damit zu einem duennen Einstiegspunkt, der auf gemeinsam gepflegte Bausteine verweist, statt jede Zeile selbst zu enthalten. Vier Varianten stehen zur Verfuegung: local fuer Dateien im selben Repository, project fuer andere Repositories in derselben GitLab-Instanz, remote fuer beliebige HTTPS-URLs und template fuer die von GitLab mitgelieferten Standardvorlagen.
2. include:local: Eine grosse Pipeline in mehrere Dateien aufteilen
Die einfachste Form ist include:local. Sie referenziert eine Datei im selben Repository und im selben Git-Ref wie die aufrufende .gitlab-ci.yml. Das ist besonders dann sinnvoll, wenn eine einzelne Pipeline-Datei unuebersichtlich gross geworden ist, etwa weil sie Build-, Test-, Security- und Deploy-Stages fuer mehrere Microservices in einem Monorepo enthaelt. Statt einer 800 Zeilen langen Datei entstehen dann mehrere fachlich getrennte Dateien unter einem Verzeichnis wie .gitlab/ci/, die sich einzeln uebersichtlich reviewen lassen.
Wichtig ist, dass include:local rein strukturell wirkt und keine Versionierung ueber Repository-Grenzen hinweg bietet, weil Datei und Aufrufer immer aus demselben Commit stammen. Das macht es ideal fuer die Aufteilung innerhalb eines Projekts, aber ungeeignet, wenn mehrere unabhaengige Repositories dieselbe Logik teilen sollen. Fuer den Team-internen Gebrauch reicht es aber vollkommen aus, um Lesbarkeit und Review-Faehigkeit deutlich zu verbessern, ohne die Kontrolle ueber die Pipeline aus der Hand zu geben.
# .gitlab-ci.yml
include:
- local: '.gitlab/ci/build.yml'
- local: '.gitlab/ci/test.yml'
- local: '.gitlab/ci/deploy.yml'
stages:
- build
- test
- deploy
3. include:project: Pipeline-Bausteine aus einem anderen Repository beziehen
include:project geht einen entscheidenden Schritt weiter und erlaubt es, eine Datei aus einem komplett anderen GitLab-Projekt einzubinden, solange der ausfuehrende Nutzer oder Runner Leseberechtigung auf das Quellprojekt hat. Genau das ist die Grundlage fuer eine zentrale CI-Bibliothek: Ein dediziertes Repository, zum Beispiel devops/ci-templates, enthaelt fertige Jobs fuer PHP-Builds, Composer-Caching, PHPUnit-Ausfuehrung oder Deployment auf einen bestimmten Servertyp, und jedes Anwendungsprojekt bindet daraus genau das ein, was es braucht.
Der grosse Vorteil gegenueber Kopieren ist die zentrale Pflege: Wird ein Job in der Bibliothek korrigiert oder erweitert, zum Beispiel um einen neuen PHP-8.4-Compatibility-Check, profitieren alle einbindenden Projekte automatisch davon, sobald sie den referenzierten ref aktualisieren oder wenn kein fester ref gesetzt ist, sogar sofort beim naechsten Pipeline-Lauf. Das birgt allerdings auch ein Risiko: Ohne bewusste Versionierung kann eine Aenderung an der Bibliothek unbeabsichtigt alle abhaengigen Pipelines gleichzeitig brechen, weshalb Versionierung ein eigenes Thema weiter unten in diesem Artikel verdient.
# .gitlab-ci.yml eines Anwendungsprojekts
include:
- project: 'devops/ci-templates'
ref: 'v2.4.0'
file:
- '/php/build.yml'
- '/php/phpunit.yml'
- '/deploy/ssh-deploy.yml'
4. include:remote: Externe URLs und ihre Grenzen
include:remote laedt eine YAML-Datei ueber eine beliebige, oeffentlich erreichbare HTTPS-URL. Das ist praktisch, um Vorlagen von Drittanbietern einzubinden oder Pipeline-Bausteine ueber ein internes, nicht in GitLab liegendes Content-Delivery-System bereitzustellen. In der Praxis ist es aber die am seltensten passende Wahl fuer eine interne Template-Bibliothek, weil GitLab-eigene Authentifizierung dabei nicht greift: Die URL muss ohne GitLab-Token erreichbar sein, was interne, zugriffsbeschraenkte Templates ueber diesen Weg praktisch ausschliesst.
Ein weiterer Unterschied zu include:project ist das Fehlen einer sauberen Versionsanbindung an Git-Refs. Zwar laesst sich eine URL mit einem Tag oder Commit-Hash parametrisieren, etwa raw.githubusercontent.com/org/repo/v1.2.0/template.yml, aber GitLab selbst prueft nicht, ob die referenzierte Datei zu einem bestimmten, ueberpruefbaren Zustand gehoert. Fuer oeffentliche, gut versionierte Vorlagen wie Community-Security-Scanner ist das akzeptabel, fuer unternehmensinterne CI-Bausteine mit Zugriffsbeschraenkung ist include:project die deutlich robustere und sicherere Wahl.
# .gitlab-ci.yml
include:
- remote: 'https://raw.githubusercontent.com/example-org/ci-lib/v1.3.0/php-lint.yml'
5. include:template: GitLab-eigene Standardvorlagen nutzen
include:template bindet eine der vorgefertigten Vorlagen ein, die GitLab selbst als Teil der Plattform mitliefert, etwa fuer Security-Scans, Auto-DevOps-Stages oder Sprach-spezifische Build-Patterns. Diese Vorlagen liegen im internen GitLab-Verzeichnis gitlab-org/gitlab und werden mit jeder GitLab-Version aktualisiert, ohne dass Nutzer sie selbst pflegen muessen. Sie sind der schnellste Weg, um etablierte Security- oder Compliance-Funktionen ohne eigenen Implementierungsaufwand in eine Pipeline zu bringen.
Der Nachteil ist geringere Kontrolle: Aktualisiert GitLab eine Template-Vorlage, kann sich das Verhalten der eigenen Pipeline ohne eigenes Zutun aendern, sobald kein fester GitLab-Versionsbezug vorliegt. In der Praxis kombinieren viele Teams deshalb include:template fuer GitLab-eigene Standardfunktionen wie SAST oder Dependency Scanning mit include:project fuer alles, was das Team selbst entwickelt und versioniert. Diese Kombination nutzt die Staerken beider Ansaetze, ohne auf die jeweils andere Flexibilitaet verzichten zu muessen.
# .gitlab-ci.yml
include:
- template: 'Security/SAST.gitlab-ci.yml'
- project: 'devops/ci-templates'
file: '/php/build.yml'
6. Aufbau einer zentralen CI-Template-Bibliothek
Eine ausgereifte Template-Bibliothek ist selbst ein Software-Projekt und verdient dieselbe Sorgfalt wie Anwendungscode. Sinnvoll ist eine klare Verzeichnisstruktur pro Sprache oder Technologie, zum Beispiel /php, /node, /docker und /deploy, sowie eine README mit Beispielen, wie jeder Baustein eingebunden wird. Jeder Job in der Bibliothek sollte moeglichst generisch formuliert sein und ueber Variablen wie APP_NAME oder DEPLOY_TARGET parametrisierbar sein, statt projektspezifische Annahmen fest zu verdrahten.
Fuer den Rollout empfiehlt sich ein schrittweises Vorgehen: Zunaechst ein Pilotprojekt vollstaendig auf die Bibliothek umstellen, dann ein zweites, drittes Projekt folgen lassen und erst danach die Migration aller verbleibenden Repositories angehen. So lassen sich Kinderkrankheiten der Templates fruehzeitig erkennen, bevor hundert Projekte gleichzeitig betroffen waeren. Ein eigenes Merge-Request-Template im Bibliotheks-Repository, das an Aenderungen erinnert, alle abhaengigen Projekte zu informieren, verhindert zusaetzlich boese Ueberraschungen bei breaking changes.
7. Versionierung: Warum ref niemals main sein sollte
Der haeufigste Fehler beim Einsatz von include:project ist, den ref-Parameter wegzulassen oder fest auf main zu setzen. Damit zieht jede Pipeline bei jedem Lauf automatisch den aktuellsten Stand der Bibliothek, was bei einer fehlerhaften Aenderung sofort alle abhaengigen Projekte gleichzeitig lahmlegen kann. Stattdessen sollte jedes Anwendungsprojekt einen festen Tag referenzieren, etwa v2.4.0, und diesen bewusst und getestet aktualisieren, wenn eine neue Version der Bibliothek verfuegbar ist.
Semantische Versionierung eignet sich hier hervorragend: Patch-Releases fuer Bugfixes ohne Verhaltensaenderung, Minor-Releases fuer neue, opt-in nutzbare Jobs und Major-Releases fuer Breaking Changes wie umbenannte Variablen oder veraenderte Job-Namen. Ein CHANGELOG.md im Bibliotheks-Repository, das jede Version dokumentiert, macht die Migration fuer abhaengige Teams nachvollziehbar. So bleibt die zentrale Bibliothek ein Produktivitaetsgewinn statt einer Quelle fuer unvorhersehbare Pipeline-Ausfaelle.
8. Parametrisierung mit spec:inputs und Kombination mit rules
Seit GitLab 15.11 koennen Templates ueber einen spec:inputs Block am Dateianfang typisierte Eingabeparameter definieren, die beim include mit übergeben werden. Das ersetzt die frueher gaengige, fehleranfaellige Praxis, Variablen einfach an anderer Stelle in der Pipeline zu setzen und zu hoffen, dass der eingebundene Job sie zufaellig verwendet. Mit inputs wird stattdessen explizit sichtbar, welche Werte ein Template erwartet, inklusive Default-Werten und erlaubter Optionen.
Kombiniert mit rules innerhalb der eingebundenen Jobs lassen sich Bausteine bauen, die sich je nach aufrufendem Kontext unterschiedlich verhalten, etwa ein Deploy-Job, der nur auf dem main-Branch tatsaechlich deployt und auf Feature-Branches lediglich einen Dry-Run durchfuehrt. Diese Kombination aus parametrisierten Templates und kontextabhaengigen Regeln ist der Kern dessen, was eine CI-Bibliothek von einer blossen Sammlung kopierter YAML-Fragmente unterscheidet: Sie verhaelt sich wie eine echte, konfigurierbare Softwarekomponente.
# devops/ci-templates: /deploy/ssh-deploy.yml
spec:
inputs:
environment:
default: 'staging'
options: ['staging', 'production']
---
deploy:
stage: deploy
script:
- echo "Deploying to $[[ inputs.environment ]]"
rules:
- if: '$CI_COMMIT_BRANCH == "main" && $[[ inputs.environment ]] == "production"'
- if: '$CI_COMMIT_BRANCH != "main"'
when: manual
9. Best Practices, Fallstricke und Vergleich der vier Varianten
In der Praxis bewaehrt sich eine klare Faustregel: include:template fuer alles, was GitLab selbst als Standardfunktion anbietet, include:project fuer selbst entwickelte, versionierte Team-Bausteine, include:local fuer die reine Aufteilung grosser Dateien innerhalb eines Repositories und include:remote nur fuer oeffentliche, extern gepflegte Vorlagen ohne Zugriffsbeschraenkung. Wer alle vier Varianten wahllos mischt, verliert schnell den Ueberblick, welche Aenderung an welcher Stelle vorgenommen werden muss.
Ein haeufiger Fallstrick ist zirkulaeres Includieren, wenn zwei Bibliotheks-Dateien sich gegenseitig referenzieren, was GitLab mit einer klaren Fehlermeldung ablehnt. Ebenso wichtig ist die maximale Tiefe von 150 eingebundenen Dateien pro Pipeline, die in sehr grossen Monorepo-Setups relevant werden kann. Die folgende Tabelle fasst die vier Varianten mit ihren wichtigsten Eigenschaften zusammen, um bei der Wahl der richtigen Methode fuer den jeweiligen Anwendungsfall zu unterstuetzen.
| Variante | Quelle | Versionierung | Typischer Einsatz |
|---|---|---|---|
| include:local | Gleiches Repository, gleicher Ref | Automatisch ueber Commit | Grosse Pipelines in Dateien aufteilen |
| include:project | Anderes GitLab-Projekt | Via ref (Tag/Branch/SHA) | Zentrale, team-eigene CI-Bibliothek |
| include:remote | Beliebige HTTPS-URL | Nur manuell ueber URL-Pfad | Oeffentliche, extern gepflegte Vorlagen |
| include:template | GitLab-Standardvorlagen | An GitLab-Version gekoppelt | Security-Scans, Auto-DevOps-Bausteine |
Mironsoft
CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung
Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?
Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.
Pipeline-Review
Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.
Zero-Downtime-Deployment
Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.
CI/CD-Automatisierung
Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.
10. Zusammenfassung
GitLab CI include: Das Wichtigste auf einen Blick
Vier Varianten
local, project, remote und template decken jeden Anwendungsfall von Datei-Aufteilung bis externer Vorlage ab.
Zentrale Bibliothek
include:project mit festem ref macht ein dediziertes Repository zur Single Source of Truth fuer CI-Jobs.
Versionierung zaehlt
Ein fester Tag statt main verhindert, dass eine Bibliotheks-Aenderung alle Projekte gleichzeitig bricht.
Parametrisierung
spec:inputs macht Templates zu konfigurierbaren Bausteinen statt starren Kopiervorlagen.