[{"data":1,"prerenderedAt":838},["ShallowReactive",2],{"/de-de/blog/migration-from-azure-devops-to-gitlab":3,"navigation-de-de":41,"banner-de-de":464,"footer-de-de":475,"blog-post-authors-de-de-Evgeny Rudinsky|Michael Leopard":710,"blog-related-posts-de-de-migration-from-azure-devops-to-gitlab":736,"blog-promotions-de-de":776,"next-steps-de-de":828},{"id":4,"title":5,"authorSlugs":6,"authors":9,"body":12,"category":13,"categorySlug":13,"config":14,"content":18,"date":23,"description":26,"extension":27,"externalUrl":28,"featured":15,"heroImage":24,"isFeatured":15,"meta":29,"navigation":15,"path":30,"publishedDate":23,"rawbody":31,"seo":32,"slug":17,"stem":36,"tagSlugs":37,"tags":39,"template":16,"updatedDate":28,"__hash__":40},"blogPosts/de-de/blog/migration-from-azure-devops-to-gitlab.yml","Migration von Azure DevOps zu GitLab systematisch planen",[7,8],"evgeny-rudinsky","michael-leopard",[10,11],"Evgeny Rudinsky","Michael Leopard","Enterprise-Migrationen in regulierten Branchen wie Finanzwesen und Automobilindustrie erfordern systematisches Risikomanagement gemäß NIS2-Richtlinie Artikel 21. Die mehrstufige Migrationsstruktur mit Testläufen vor Produktions-Wellen und kontrollierten Change-Freezes demonstriert Business-Continuity-Management in der Praxis. GitLab Professional Services organisiert Migrationen in Wellen von 200-300 Projekten, um Komplexität zu managen und API-Rate-Limits zu respektieren.\n\n## Überblick\n\nGitLab bietet [Congregate](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/) (maintained by GitLab Professional Services) und [eingebauten Git-Repository-Import](https://docs.gitlab.com/user/project/import/repo_by_url/) für Migrationen von Azure DevOps (ADO). Beide Optionen unterstützen Repository-basierte oder Bulk-Migration und erhalten Git-Commit-History, Branches und Tags. Mit Congregate werden zusätzliche Assets wie Wikis, Work Items, CI/CD-Variablen, Container-Images, Packages und Pipelines migriert (siehe [Feature-Matrix](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/ado-migration-features-matrix.md)).\n\nEnterprises folgen typischerweise einem mehrstufigen Ansatz:\n\n- Repositories von ADO zu GitLab migrieren (Congregate oder eingebauter Import)\n- Pipelines von Azure Pipelines zu GitLab CI/CD migrieren\n- Verbleibende Assets wie Boards, Work Items und Artifacts zu GitLab Issues, Epics und Package/Container Registries migrieren\n\nMehrstufige Migrationsphasen (siehe [Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#overview)):\n\n- Prerequisites (IdP, Runners, Change Management)\n- Migration Phase (Source Code, History, Work Items)\n- Post-Migration (Pipelines, Assets, Security)\n\n## Migration planen\n\n**Zentrale Planungsfragen:**\n\n- Wie schnell muss die Migration abgeschlossen werden?\n- Was genau wird migriert?\n- Wer führt die Migration durch?\n- Welche Organisationsstruktur wird in GitLab benötigt?\n- Welche Einschränkungen, Limitierungen oder Fallstricke müssen berücksichtigt werden?\n\nDie Timeline bestimmt weitgehend den Migrationsansatz. Identifizierung von Champions oder Teams mit ADO- und GitLab-Erfahrung unterstützt Adoption und Guidance.\n\n**Inventar erstellen:**\n\nDas GitLab Professional Services [Evaluate](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate#beta-azure-devops)-Tool produziert ein vollständiges Inventar der Azure DevOps Organisation: Repositories, PR-Counts, Contributors, Pipelines, Work Items, CI/CD-Variablen. Bei Professional Services Engagements wird dieser Report mit Engagement Manager oder Technical Architect geteilt für Migrationsplanung.\n\nMigrations-Timing wird primär bestimmt durch Pull-Request-Count, Repository-Größe und Contribution-Menge. Beispiel: 1.000 kleine Repositories mit wenigen PRs migrieren schneller als wenige Repositories mit Zehntausenden PRs. Inventar-Daten ermöglichen Aufwands-Schätzung und Test-Run-Planung.\n\nIn Professional Services Engagements werden Migrationen in Wellen von 200-300 Projekten organisiert, um Komplexität zu managen und API-Rate-Limits zu respektieren (sowohl [GitLab](https://docs.gitlab.com/security/rate_limits/) als auch [ADO](https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits?view=azure-devops)).\n\n**Tool-Auswahl:**\n\nGitLabs [eingebauter Repository-Importer](https://docs.gitlab.com/user/project/import/repo_by_url/) migriert Git-Repositories (Commits, Branches, Tags) einzeln. Congregate erhält Pull Requests (in GitLab: Merge Requests), Kommentare und Metadata; der eingebaute Import fokussiert auf Git-Daten (History, Branches, Tags).\n\nAssets die typischerweise separate Migration oder manuelle Neuerstellung erfordern:\n\n- Azure Pipelines → GitLab CI/CD Pipelines (siehe [CI/CD YAML](https://docs.gitlab.com/ci/yaml/) oder [CI/CD Components](https://docs.gitlab.com/ci/components/)). Alternativ: AI-basierte Pipeline-Konvertierung in Congregate.\n- Work Items und Boards → GitLab Issues, Epics, Issue Boards\n- Artifacts, Container-Images (ACR) → GitLab Package/Container Registry\n- Service Hooks, externe Integrationen → in GitLab neu erstellen\n\n[Permissions-Modelle](https://docs.gitlab.com/user/permissions/) unterscheiden sich zwischen ADO und GitLab. Permissions-Mapping planen statt exakter Preservation zu erwarten.\n\n**Organisationsstruktur in GitLab:**\n\nEmpfohlener Ansatz: ADO-Organisationen auf GitLab-Groups mappen (nicht viele kleine Groups). Migration als Gelegenheit nutzen, GitLab-Struktur zu rationalisieren (siehe [Struktur-Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#planning-your-migration)):\n\n- **ADO Organization** → GitLab Top-level Group\n- **ADO Project** → GitLab Subgroup (optional)\n- **ADO Repository** → GitLab Project\n\nWeitere Empfehlungen:\n\n- Subgroups und Project-Level-Permissions für verwandte Repositories nutzen\n- Zugriff über GitLab Groups und Group-Membership managen\n- GitLab [Permissions](https://docs.gitlab.com/user/permissions/) und [SAML Group Links](https://docs.gitlab.com/user/group/saml_sso/group_sync/) für Enterprise-RBAC-Modell evaluieren\n\n**ADO Work Items Migration:**\n\nADO Boards und Work Items mappen zu GitLab Issues, Epics und Issue Boards. ADO Epics und Features werden GitLab Epics. Andere Work-Item-Typen (User Stories, Tasks, Bugs) werden project-scoped Issues. Standard-Felder bleiben erhalten; ausgewählte Custom Fields können migriert werden. Parent-Child-Relationships bleiben erhalten. Links zu Pull Requests werden zu Merge-Request-Links konvertiert.\n\n![Migration eines Work Items zu GitLab Issue](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764769188/ztesjnxxfbwmfmtckyga.png)\n\n**Pipelines-Migration:**\n\nCongregate bietet [AI-basierte Konvertierung](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/merge_requests/1298) für multi-stage YAML-Pipelines von Azure DevOps zu GitLab CI/CD. Die automatisierte Konvertierung funktioniert optimal für einfache Single-File-Pipelines und liefert einen funktionalen Ausgangspunkt, nicht ein produktionsreifes `.gitlab-ci.yml`-File. Das Tool generiert funktional äquivalente GitLab-Pipelines zur weiteren Optimierung.\n\n- Konvertiert Azure Pipelines YAML zu `.gitlab-ci.yml` automatisch\n- Geeignet für straightforward Single-File-Pipeline-Konfigurationen\n- Liefert Boilerplate zur Migrations-Beschleunigung, nicht finales Production-Artifact\n- Erfordert Review und Anpassung für komplexe Szenarien, Custom Tasks oder Enterprise-Requirements\n- Unterstützt keine Azure DevOps Classic Release Pipelines\n\nRepository-Owner sollten [GitLab CI/CD Dokumentation](https://docs.gitlab.com/ci/) konsultieren für weitere Pipeline-Optimierung nach initialer Konvertierung.\n\n## Migrationen durchführen\n\nNach der Planung werden Migrationen in Stages durchgeführt, beginnend mit Trial Runs. Trial Migrations identifizieren organisations-spezifische Issues frühzeitig und ermöglichen Duration-Messung, Outcome-Validierung und Approach-Finetuning vor Production.\n\n**Was Trial Migrations validieren:**\n\n- Ob Repository und Assets erfolgreich migrieren (History, Branches, Tags; plus MRs/Comments bei Congregate)\n- Ob Destination sofort nutzbar ist (Permissions, Runners, CI/CD-Variablen, Integrationen)\n- Wie lange jeder Batch benötigt für Schedule- und Stakeholder-Expectations\n\n**Downtime-Guidance:**\n\nGitLabs eingebauter Git-Import und Congregate erfordern inhärent keine Downtime. Für Production-Wellen: Changes in ADO freezen (Branch-Protections oder Read-only), um verpasste Commits, PR-Updates oder mid-migration Work Items zu vermeiden. Trial Runs erfordern keine Freezes.\n\n**Batching-Guidance:**\n\nTrial-Batches back-to-back durchführen für kürzere elapsed Time. Teams validieren Results asynchron. Geplante Group/Subgroup-Struktur für Batch-Definition nutzen und API-Rate-Limits respektieren.\n\n**Empfohlene Schritte:**\n\n1. Test-Destination in GitLab erstellen (GitLab.com: dedicated Group/Namespace; Self-managed: Top-level Group)\n2. Authentication vorbereiten (Azure DevOps PAT, GitLab Personal Access Token mit api und read_repository Scopes)\n3. Trial Migrations durchführen (Repos only: eingebauter Import; Repos + PRs/MRs: Congregate)\n4. Post-Trial Follow-up (Repo-History, Branches, Tags, Merge Requests, Issues/Epics, Labels, Relationships verifizieren)\n5. Permissions/Roles, Protected Branches, Runners/Tags, Variables/Secrets, Integrations/Webhooks prüfen\n6. Pipelines (`.gitlab-ci.yml`) oder konvertierte Pipelines validieren\n7. User-Validierung für Functionality und Data-Fidelity\n8. Production-Migrationen in Waves durchführen (Change-Freezes in ADO erzwingen, Progress und Logs monitoren)\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/ibIXGfrVbi4?si=ZxOVnXjCF-h4Ne0N\" frameborder=\"0\" allowfullscreen=\"true\">\u003C/iframe>\n\u003C/figure>\n\n## Zentrale ADO-zu-GitLab-Mappings\n\nWichtigste Struktur-Mappings für Migrationsplanung:\n\n- **ADO Organization** → GitLab Group (Top-level Namespace)\n- **ADO Project** → GitLab Group oder Subgroup (Permissions-Boundary)\n- **ADO Repository** → GitLab Project (Git-Repo plus Issues, CI/CD, Wiki)\n- **Pull Request** → Merge Request (Code Review, Approvals)\n- **Azure Pipelines** → GitLab CI/CD (`.gitlab-ci.yml`)\n- **Agent Pools** → GitLab Runners (Job-Execution)\n- **Work Items** → GitLab Issues/Epics (Planning-Funktionalität)\n- **Variable Groups** → CI/CD Variables (Project/Group/Instance Level)\n\nFür vollständige Terminologie-Referenztabelle mit allen Mappings siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\n## Professional Services Migrationsmuster\n\nGitLab Professional Services nutzt bewährte Muster für Enterprise-Migrationen:\n\n**Systematische Planung:** Evaluate-Tool liefert vollständiges Inventar (Repositories, PRs, Contributors, Pipelines, Work Items). Klassifikation nach Komplexität (Work Items = Planning-Team-Involvement; Pipelines = Conversion-Requirements) ermöglicht Timeline-Schätzung und Batch-Definition.\n\n**Controlled Execution:** Wellen von 200-300 Projekten managen Komplexität und respektieren API-Rate-Limits. Trial-Migrationen vor Production-Waves identifizieren Issues frühzeitig. Change-Freezes in ADO während Production-Wellen vermeiden mid-migration Updates.\n\n**Risikominimierung:** Multi-Phase-Ansatz (Prerequisites → Migration → Post-migration) mit Validierungs-Checkpoints an jeder Phase. Trial-Runs ermöglichen asynchrone Team-Validierung ohne Production-Impact.\n\n## Praktische Implementierung\n\nFür vollständige Implementierungsdetails, Konfigurationsbeispiele, Pipeline-Konvertierungs-Patterns und detaillierte Post-Migration-Checklisten siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\nWeitere Professional Services Ressourcen:\n\n- [Professional Services Full Catalog](https://about.gitlab.com/professional-services/catalog/)\n- [Congregate Migration Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/)\n- [Evaluate Inventory Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate)\n","engineering",{"featured":15,"template":16,"slug":17},true,"BlogPost","migration-from-azure-devops-to-gitlab",{"category":13,"tags":19,"body":12,"date":23,"heroImage":24,"authors":25,"title":5,"description":26},[20,21,22],"tutorial","git","CI/CD","2025-12-03","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png",[10,11],"Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.","yml",null,{},"/de-de/blog/migration-from-azure-devops-to-gitlab","seo:\n  title: 'Migration von Azure DevOps zu GitLab systematisch planen'\n  description: 'Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.'\n  ogTitle: 'Migration von Azure DevOps zu GitLab systematisch planen'\n  ogDescription: 'Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.'\n  ogUrl: 'https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab'\n  ogSiteName: 'https://about.gitlab.com'\n  ogType: 'article'\n  canonicalUrls: 'https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab'\n  ogImage: 'https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png'\ncontent:\n  category: engineering\n  tags:\n    - tutorial\n    - git\n    - CI/CD\n  body: |\n    Enterprise-Migrationen in regulierten Branchen wie Finanzwesen und Automobilindustrie erfordern systematisches Risikomanagement gemäß NIS2-Richtlinie Artikel 21. Die mehrstufige Migrationsstruktur mit Testläufen vor Produktions-Wellen und kontrollierten Change-Freezes demonstriert Business-Continuity-Management in der Praxis. GitLab Professional Services organisiert Migrationen in Wellen von 200-300 Projekten, um Komplexität zu managen und API-Rate-Limits zu respektieren.\n\n    ## Überblick\n\n    GitLab bietet [Congregate](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/) (maintained by GitLab Professional Services) und [eingebauten Git-Repository-Import](https://docs.gitlab.com/user/project/import/repo_by_url/) für Migrationen von Azure DevOps (ADO). Beide Optionen unterstützen Repository-basierte oder Bulk-Migration und erhalten Git-Commit-History, Branches und Tags. Mit Congregate werden zusätzliche Assets wie Wikis, Work Items, CI/CD-Variablen, Container-Images, Packages und Pipelines migriert (siehe [Feature-Matrix](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/ado-migration-features-matrix.md)).\n\n    Enterprises folgen typischerweise einem mehrstufigen Ansatz:\n\n    - Repositories von ADO zu GitLab migrieren (Congregate oder eingebauter Import)\n    - Pipelines von Azure Pipelines zu GitLab CI/CD migrieren\n    - Verbleibende Assets wie Boards, Work Items und Artifacts zu GitLab Issues, Epics und Package/Container Registries migrieren\n\n    Mehrstufige Migrationsphasen (siehe [Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#overview)):\n\n    - Prerequisites (IdP, Runners, Change Management)\n    - Migration Phase (Source Code, History, Work Items)\n    - Post-Migration (Pipelines, Assets, Security)\n\n    ## Migration planen\n\n    **Zentrale Planungsfragen:**\n\n    - Wie schnell muss die Migration abgeschlossen werden?\n    - Was genau wird migriert?\n    - Wer führt die Migration durch?\n    - Welche Organisationsstruktur wird in GitLab benötigt?\n    - Welche Einschränkungen, Limitierungen oder Fallstricke müssen berücksichtigt werden?\n\n    Die Timeline bestimmt weitgehend den Migrationsansatz. Identifizierung von Champions oder Teams mit ADO- und GitLab-Erfahrung unterstützt Adoption und Guidance.\n\n    **Inventar erstellen:**\n\n    Das GitLab Professional Services [Evaluate](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate#beta-azure-devops)-Tool produziert ein vollständiges Inventar der Azure DevOps Organisation: Repositories, PR-Counts, Contributors, Pipelines, Work Items, CI/CD-Variablen. Bei Professional Services Engagements wird dieser Report mit Engagement Manager oder Technical Architect geteilt für Migrationsplanung.\n\n    Migrations-Timing wird primär bestimmt durch Pull-Request-Count, Repository-Größe und Contribution-Menge. Beispiel: 1.000 kleine Repositories mit wenigen PRs migrieren schneller als wenige Repositories mit Zehntausenden PRs. Inventar-Daten ermöglichen Aufwands-Schätzung und Test-Run-Planung.\n\n    In Professional Services Engagements werden Migrationen in Wellen von 200-300 Projekten organisiert, um Komplexität zu managen und API-Rate-Limits zu respektieren (sowohl [GitLab](https://docs.gitlab.com/security/rate_limits/) als auch [ADO](https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits?view=azure-devops)).\n\n    **Tool-Auswahl:**\n\n    GitLabs [eingebauter Repository-Importer](https://docs.gitlab.com/user/project/import/repo_by_url/) migriert Git-Repositories (Commits, Branches, Tags) einzeln. Congregate erhält Pull Requests (in GitLab: Merge Requests), Kommentare und Metadata; der eingebaute Import fokussiert auf Git-Daten (History, Branches, Tags).\n\n    Assets die typischerweise separate Migration oder manuelle Neuerstellung erfordern:\n\n    - Azure Pipelines → GitLab CI/CD Pipelines (siehe [CI/CD YAML](https://docs.gitlab.com/ci/yaml/) oder [CI/CD Components](https://docs.gitlab.com/ci/components/)). Alternativ: AI-basierte Pipeline-Konvertierung in Congregate.\n    - Work Items und Boards → GitLab Issues, Epics, Issue Boards\n    - Artifacts, Container-Images (ACR) → GitLab Package/Container Registry\n    - Service Hooks, externe Integrationen → in GitLab neu erstellen\n\n    [Permissions-Modelle](https://docs.gitlab.com/user/permissions/) unterscheiden sich zwischen ADO und GitLab. Permissions-Mapping planen statt exakter Preservation zu erwarten.\n\n    **Organisationsstruktur in GitLab:**\n\n    Empfohlener Ansatz: ADO-Organisationen auf GitLab-Groups mappen (nicht viele kleine Groups). Migration als Gelegenheit nutzen, GitLab-Struktur zu rationalisieren (siehe [Struktur-Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#planning-your-migration)):\n\n    - **ADO Organization** → GitLab Top-level Group\n    - **ADO Project** → GitLab Subgroup (optional)\n    - **ADO Repository** → GitLab Project\n\n    Weitere Empfehlungen:\n\n    - Subgroups und Project-Level-Permissions für verwandte Repositories nutzen\n    - Zugriff über GitLab Groups und Group-Membership managen\n    - GitLab [Permissions](https://docs.gitlab.com/user/permissions/) und [SAML Group Links](https://docs.gitlab.com/user/group/saml_sso/group_sync/) für Enterprise-RBAC-Modell evaluieren\n\n    **ADO Work Items Migration:**\n\n    ADO Boards und Work Items mappen zu GitLab Issues, Epics und Issue Boards. ADO Epics und Features werden GitLab Epics. Andere Work-Item-Typen (User Stories, Tasks, Bugs) werden project-scoped Issues. Standard-Felder bleiben erhalten; ausgewählte Custom Fields können migriert werden. Parent-Child-Relationships bleiben erhalten. Links zu Pull Requests werden zu Merge-Request-Links konvertiert.\n\n    ![Migration eines Work Items zu GitLab Issue](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764769188/ztesjnxxfbwmfmtckyga.png)\n\n    **Pipelines-Migration:**\n\n    Congregate bietet [AI-basierte Konvertierung](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/merge_requests/1298) für multi-stage YAML-Pipelines von Azure DevOps zu GitLab CI/CD. Die automatisierte Konvertierung funktioniert optimal für einfache Single-File-Pipelines und liefert einen funktionalen Ausgangspunkt, nicht ein produktionsreifes `.gitlab-ci.yml`-File. Das Tool generiert funktional äquivalente GitLab-Pipelines zur weiteren Optimierung.\n\n    - Konvertiert Azure Pipelines YAML zu `.gitlab-ci.yml` automatisch\n    - Geeignet für straightforward Single-File-Pipeline-Konfigurationen\n    - Liefert Boilerplate zur Migrations-Beschleunigung, nicht finales Production-Artifact\n    - Erfordert Review und Anpassung für komplexe Szenarien, Custom Tasks oder Enterprise-Requirements\n    - Unterstützt keine Azure DevOps Classic Release Pipelines\n\n    Repository-Owner sollten [GitLab CI/CD Dokumentation](https://docs.gitlab.com/ci/) konsultieren für weitere Pipeline-Optimierung nach initialer Konvertierung.\n\n    ## Migrationen durchführen\n\n    Nach der Planung werden Migrationen in Stages durchgeführt, beginnend mit Trial Runs. Trial Migrations identifizieren organisations-spezifische Issues frühzeitig und ermöglichen Duration-Messung, Outcome-Validierung und Approach-Finetuning vor Production.\n\n    **Was Trial Migrations validieren:**\n\n    - Ob Repository und Assets erfolgreich migrieren (History, Branches, Tags; plus MRs/Comments bei Congregate)\n    - Ob Destination sofort nutzbar ist (Permissions, Runners, CI/CD-Variablen, Integrationen)\n    - Wie lange jeder Batch benötigt für Schedule- und Stakeholder-Expectations\n\n    **Downtime-Guidance:**\n\n    GitLabs eingebauter Git-Import und Congregate erfordern inhärent keine Downtime. Für Production-Wellen: Changes in ADO freezen (Branch-Protections oder Read-only), um verpasste Commits, PR-Updates oder mid-migration Work Items zu vermeiden. Trial Runs erfordern keine Freezes.\n\n    **Batching-Guidance:**\n\n    Trial-Batches back-to-back durchführen für kürzere elapsed Time. Teams validieren Results asynchron. Geplante Group/Subgroup-Struktur für Batch-Definition nutzen und API-Rate-Limits respektieren.\n\n    **Empfohlene Schritte:**\n\n    1. Test-Destination in GitLab erstellen (GitLab.com: dedicated Group/Namespace; Self-managed: Top-level Group)\n    2. Authentication vorbereiten (Azure DevOps PAT, GitLab Personal Access Token mit api und read_repository Scopes)\n    3. Trial Migrations durchführen (Repos only: eingebauter Import; Repos + PRs/MRs: Congregate)\n    4. Post-Trial Follow-up (Repo-History, Branches, Tags, Merge Requests, Issues/Epics, Labels, Relationships verifizieren)\n    5. Permissions/Roles, Protected Branches, Runners/Tags, Variables/Secrets, Integrations/Webhooks prüfen\n    6. Pipelines (`.gitlab-ci.yml`) oder konvertierte Pipelines validieren\n    7. User-Validierung für Functionality und Data-Fidelity\n    8. Production-Migrationen in Waves durchführen (Change-Freezes in ADO erzwingen, Progress und Logs monitoren)\n\n    \u003Cfigure class=\"video_container\">\n      \u003Ciframe src=\"https://www.youtube.com/embed/ibIXGfrVbi4?si=ZxOVnXjCF-h4Ne0N\" frameborder=\"0\" allowfullscreen=\"true\">\u003C/iframe>\n    \u003C/figure>\n\n    ## Zentrale ADO-zu-GitLab-Mappings\n\n    Wichtigste Struktur-Mappings für Migrationsplanung:\n\n    - **ADO Organization** → GitLab Group (Top-level Namespace)\n    - **ADO Project** → GitLab Group oder Subgroup (Permissions-Boundary)\n    - **ADO Repository** → GitLab Project (Git-Repo plus Issues, CI/CD, Wiki)\n    - **Pull Request** → Merge Request (Code Review, Approvals)\n    - **Azure Pipelines** → GitLab CI/CD (`.gitlab-ci.yml`)\n    - **Agent Pools** → GitLab Runners (Job-Execution)\n    - **Work Items** → GitLab Issues/Epics (Planning-Funktionalität)\n    - **Variable Groups** → CI/CD Variables (Project/Group/Instance Level)\n\n    Für vollständige Terminologie-Referenztabelle mit allen Mappings siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\n    ## Professional Services Migrationsmuster\n\n    GitLab Professional Services nutzt bewährte Muster für Enterprise-Migrationen:\n\n    **Systematische Planung:** Evaluate-Tool liefert vollständiges Inventar (Repositories, PRs, Contributors, Pipelines, Work Items). Klassifikation nach Komplexität (Work Items = Planning-Team-Involvement; Pipelines = Conversion-Requirements) ermöglicht Timeline-Schätzung und Batch-Definition.\n\n    **Controlled Execution:** Wellen von 200-300 Projekten managen Komplexität und respektieren API-Rate-Limits. Trial-Migrationen vor Production-Waves identifizieren Issues frühzeitig. Change-Freezes in ADO während Production-Wellen vermeiden mid-migration Updates.\n\n    **Risikominimierung:** Multi-Phase-Ansatz (Prerequisites → Migration → Post-migration) mit Validierungs-Checkpoints an jeder Phase. Trial-Runs ermöglichen asynchrone Team-Validierung ohne Production-Impact.\n\n    ## Praktische Implementierung\n\n    Für vollständige Implementierungsdetails, Konfigurationsbeispiele, Pipeline-Konvertierungs-Patterns und detaillierte Post-Migration-Checklisten siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\n    Weitere Professional Services Ressourcen:\n\n    - [Professional Services Full Catalog](https://about.gitlab.com/professional-services/catalog/)\n    - [Congregate Migration Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/)\n    - [Evaluate Inventory Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate)\n\n  date: 2025-12-03\n  heroImage: 'https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png'\n  authors:\n    - Evgeny Rudinsky\n    - Michael Leopard\n  title: 'Migration von Azure DevOps zu GitLab systematisch planen'\n  description: 'Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.'\nconfig:\n  featured: true\n  template: BlogPost\n  slug: migration-from-azure-devops-to-gitlab\n",{"title":5,"description":26,"ogTitle":5,"ogDescription":26,"ogUrl":33,"ogSiteName":34,"ogType":35,"canonicalUrls":33,"ogImage":24},"https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab","https://about.gitlab.com","article","de-de/blog/migration-from-azure-devops-to-gitlab",[20,21,38],"cicd",[20,21,22],"wEG1Bt98aRytzp2mO29qUehcpwXkWDu5p_pfBoDtX80",{"logo":42,"freeTrial":47,"sales":52,"login":57,"items":62,"search":382,"minimal":416,"duo":434,"switchNav":443,"pricingDeployment":454},{"config":43},{"href":44,"dataGaName":45,"dataGaLocation":46},"/de-de/","gitlab logo","header",{"text":48,"config":49},"Kostenlose Testversion anfordern",{"href":50,"dataGaName":51,"dataGaLocation":46},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de&glm_content=default-saas-trial/","free trial",{"text":53,"config":54},"Vertrieb kontaktieren",{"href":55,"dataGaName":56,"dataGaLocation":46},"/de-de/sales/","sales",{"text":58,"config":59},"Anmelden",{"href":60,"dataGaName":61,"dataGaLocation":46},"https://gitlab.com/users/sign_in/","sign in",[63,92,193,198,301,362],{"text":64,"config":65,"menu":67},"Plattform",{"dataNavLevelOne":66},"platform",{"type":68,"columns":69},"cards",[70,76,84],{"title":64,"description":71,"link":72},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":73,"config":74},"Die Plattform erkunden",{"href":75,"dataGaName":66,"dataGaLocation":46},"/de-de/platform/",{"title":77,"description":78,"link":79},"GitLab Duo Agent Platform","Agentische KI für den gesamten Software-Lebenszyklus",{"text":80,"config":81},"Lerne GitLab Duo kennen",{"href":82,"dataGaName":83,"dataGaLocation":46},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":85,"description":86,"link":87},"Warum GitLab?","Erfahre, warum sich Unternehmen für GitLab entscheiden",{"text":88,"config":89},"Mehr erfahren",{"href":90,"dataGaName":91,"dataGaLocation":46},"/de-de/why-gitlab/","why gitlab",{"text":93,"left":15,"config":94,"menu":96},"Produkt",{"dataNavLevelOne":95},"solutions",{"type":97,"link":98,"columns":102,"feature":172},"lists",{"text":99,"config":100},"Alle Lösungen anzeigen",{"href":101,"dataGaName":95,"dataGaLocation":46},"/de-de/solutions/",[103,127,150],{"title":104,"description":105,"link":106,"items":111},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":107},{"icon":108,"href":109,"dataGaName":110,"dataGaLocation":46},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[112,115,118,123],{"text":22,"config":113},{"href":114,"dataGaLocation":46,"dataGaName":22},"/de-de/solutions/continuous-integration/",{"text":77,"config":116},{"href":82,"dataGaLocation":46,"dataGaName":117},"gitlab duo agent platform - product menu",{"text":119,"config":120},"Quellcodeverwaltung",{"href":121,"dataGaLocation":46,"dataGaName":122},"/de-de/solutions/source-code-management/","Source Code Management",{"text":124,"config":125},"Automatische Softwarebereitstellung",{"href":109,"dataGaLocation":46,"dataGaName":126},"Automated software delivery",{"title":128,"description":129,"link":130,"items":135},"Sicherheit","Entwickle Code schneller ohne Abstriche bei der Sicherheit",{"config":131},{"href":132,"dataGaName":133,"dataGaLocation":46,"icon":134},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[136,140,145],{"text":137,"config":138},"Anwendungssicherheitstests",{"href":132,"dataGaName":139,"dataGaLocation":46},"Application security testing",{"text":141,"config":142},"Schutz der Software-Lieferkette",{"href":143,"dataGaLocation":46,"dataGaName":144},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":146,"config":147},"Software-Compliance",{"href":148,"dataGaName":149,"dataGaLocation":46},"/de-de/solutions/software-compliance/","software compliance",{"title":151,"link":152,"items":157},"Auswertung",{"config":153},{"icon":154,"href":155,"dataGaName":156,"dataGaLocation":46},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[158,162,167],{"text":159,"config":160},"Sichtbarkeit und Auswertung",{"href":155,"dataGaLocation":46,"dataGaName":161},"Visibility and Measurement",{"text":163,"config":164},"Wertstrommanagement",{"href":165,"dataGaLocation":46,"dataGaName":166},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":168,"config":169},"Analysen und Einblicke",{"href":170,"dataGaLocation":46,"dataGaName":171},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":173,"type":97,"items":174},"GitLab für",[175,181,187],{"text":176,"config":177},"Enterprise",{"icon":178,"href":179,"dataGaLocation":46,"dataGaName":180},"Building","/de-de/enterprise/","enterprise",{"text":182,"config":183},"Kleinunternehmen",{"icon":184,"href":185,"dataGaLocation":46,"dataGaName":186},"Work","/de-de/small-business/","small business",{"text":188,"config":189},"Öffentlicher Sektor",{"icon":190,"href":191,"dataGaLocation":46,"dataGaName":192},"Organization","/de-de/solutions/public-sector/","public sector",{"text":194,"config":195},"Preise",{"href":196,"dataGaName":197,"dataGaLocation":46,"dataNavLevelOne":197},"/de-de/pricing/","pricing",{"text":199,"config":200,"menu":202},"Ressourcen",{"dataNavLevelOne":201},"resources",{"type":97,"link":203,"columns":207,"feature":287},{"text":204,"config":205},"Alle Ressourcen anzeigen",{"href":206,"dataGaName":201,"dataGaLocation":46},"/de-de/resources/",[208,241,259],{"title":209,"items":210},"Erste Schritte",[211,216,221,226,231,236],{"text":212,"config":213},"Installieren",{"href":214,"dataGaName":215,"dataGaLocation":46},"/de-de/install/","install",{"text":217,"config":218},"Kurzanleitungen",{"href":219,"dataGaName":220,"dataGaLocation":46},"/de-de/get-started/","quick setup checklists",{"text":222,"config":223},"Lernen",{"href":224,"dataGaLocation":46,"dataGaName":225},"https://university.gitlab.com/","learn",{"text":227,"config":228},"Produktdokumentation",{"href":229,"dataGaName":230,"dataGaLocation":46},"https://docs.gitlab.com/","product documentation",{"text":232,"config":233},"Best-Practice-Videos",{"href":234,"dataGaName":235,"dataGaLocation":46},"/de-de/getting-started-videos/","best practice videos",{"text":237,"config":238},"Integrationen",{"href":239,"dataGaName":240,"dataGaLocation":46},"/de-de/integrations/","integrations",{"title":242,"items":243},"Entdecken",[244,249,254],{"text":245,"config":246},"Kundenerfolge",{"href":247,"dataGaName":248,"dataGaLocation":46},"/de-de/customers/","customer success stories",{"text":250,"config":251},"Blog",{"href":252,"dataGaName":253,"dataGaLocation":46},"/de-de/blog/","blog",{"text":255,"config":256},"Remote",{"href":257,"dataGaName":258,"dataGaLocation":46},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":260,"items":261},"Vernetzen",[262,267,272,277,282],{"text":263,"config":264},"GitLab Services",{"href":265,"dataGaName":266,"dataGaLocation":46},"/de-de/services/","services",{"text":268,"config":269},"Community",{"href":270,"dataGaName":271,"dataGaLocation":46},"/community/","community",{"text":273,"config":274},"Forum",{"href":275,"dataGaName":276,"dataGaLocation":46},"https://forum.gitlab.com/","forum",{"text":278,"config":279},"Veranstaltungen",{"href":280,"dataGaName":281,"dataGaLocation":46},"/events/","events",{"text":283,"config":284},"Partner",{"href":285,"dataGaName":286,"dataGaLocation":46},"/de-de/partners/","partners",{"config":288,"text":291,"image":292,"link":296},{"background":289,"textColor":290},"#2f2a6b","#fff","Perspektiven für die Softwareentwicklung der Zukunft",{"altText":293,"config":294},"The Source Promo-Karte",{"src":295},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":297,"config":298},"Aktuelles",{"href":299,"dataGaName":300,"dataGaLocation":46},"/de-de/the-source/","the source",{"text":302,"config":303,"menu":305},"Unternehmen",{"dataNavLevelOne":304},"company",{"type":97,"columns":306},[307],{"items":308},[309,314,320,322,327,332,337,342,347,352,357],{"text":310,"config":311},"Über",{"href":312,"dataGaName":313,"dataGaLocation":46},"/de-de/company/","about",{"text":315,"config":316,"footerGa":319},"Karriere",{"href":317,"dataGaName":318,"dataGaLocation":46},"/jobs/","jobs",{"dataGaName":318},{"text":278,"config":321},{"href":280,"dataGaName":281,"dataGaLocation":46},{"text":323,"config":324},"Geschäftsführung",{"href":325,"dataGaName":326,"dataGaLocation":46},"/company/team/e-group/","leadership",{"text":328,"config":329},"Team",{"href":330,"dataGaName":331,"dataGaLocation":46},"/company/team/","team",{"text":333,"config":334},"Handbuch",{"href":335,"dataGaName":336,"dataGaLocation":46},"https://handbook.gitlab.com/","handbook",{"text":338,"config":339},"Investor Relations",{"href":340,"dataGaName":341,"dataGaLocation":46},"https://ir.gitlab.com/","investor relations",{"text":343,"config":344},"Trust Center",{"href":345,"dataGaName":346,"dataGaLocation":46},"/de-de/security/","trust center",{"text":348,"config":349},"AI Transparency Center",{"href":350,"dataGaName":351,"dataGaLocation":46},"/de-de/ai-transparency-center/","ai transparency center",{"text":353,"config":354},"Newsletter",{"href":355,"dataGaName":356,"dataGaLocation":46},"/company/contact/#contact-forms","newsletter",{"text":358,"config":359},"Presse",{"href":360,"dataGaName":361,"dataGaLocation":46},"/press/","press",{"text":363,"config":364,"menu":365},"Kontakt",{"dataNavLevelOne":304},{"type":97,"columns":366},[367],{"items":368},[369,372,377],{"text":53,"config":370},{"href":55,"dataGaName":371,"dataGaLocation":46},"talk to sales",{"text":373,"config":374},"Support-Portal",{"href":375,"dataGaName":376,"dataGaLocation":46},"https://support.gitlab.com","support portal",{"text":378,"config":379},"Kundenportal",{"href":380,"dataGaName":381,"dataGaLocation":46},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":383,"login":384,"suggestions":391},"Schließen",{"text":385,"link":386},"Um Repositorys und Projekte zu durchsuchen, melde dich an bei",{"text":387,"config":388},"gitlab.com",{"href":60,"dataGaName":389,"dataGaLocation":390},"search login","search",{"text":392,"default":393},"Vorschläge",[394,396,401,403,408,413],{"text":77,"config":395},{"href":82,"dataGaName":77,"dataGaLocation":390},{"text":397,"config":398},"Codevorschläge (KI)",{"href":399,"dataGaName":400,"dataGaLocation":390},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":22,"config":402},{"href":114,"dataGaName":22,"dataGaLocation":390},{"text":404,"config":405},"GitLab auf AWS",{"href":406,"dataGaName":407,"dataGaLocation":390},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":409,"config":410},"GitLab auf Google Cloud",{"href":411,"dataGaName":412,"dataGaLocation":390},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":85,"config":414},{"href":90,"dataGaName":415,"dataGaLocation":390},"Why GitLab?",{"freeTrial":417,"mobileIcon":422,"desktopIcon":427,"secondaryButton":430},{"text":418,"config":419},"Kostenlos testen",{"href":420,"dataGaName":51,"dataGaLocation":421},"https://gitlab.com/-/trials/new/","nav",{"altText":423,"config":424},"GitLab-Symbol",{"src":425,"dataGaName":426,"dataGaLocation":421},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":423,"config":428},{"src":429,"dataGaName":426,"dataGaLocation":421},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":209,"config":431},{"href":432,"dataGaName":433,"dataGaLocation":421},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/get-started/","get started",{"freeTrial":435,"mobileIcon":439,"desktopIcon":441},{"text":436,"config":437},"Mehr über GitLab Duo erfahren",{"href":82,"dataGaName":438,"dataGaLocation":421},"gitlab duo",{"altText":423,"config":440},{"src":425,"dataGaName":426,"dataGaLocation":421},{"altText":423,"config":442},{"src":429,"dataGaName":426,"dataGaLocation":421},{"button":444,"mobileIcon":449,"desktopIcon":451},{"text":445,"config":446},"/Option",{"href":447,"dataGaName":448,"dataGaLocation":421},"#contact","switch",{"altText":423,"config":450},{"src":425,"dataGaName":426,"dataGaLocation":421},{"altText":423,"config":452},{"src":453,"dataGaName":426,"dataGaLocation":421},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":455,"mobileIcon":460,"desktopIcon":462},{"text":456,"config":457},"Zurück zur Preisübersicht",{"href":196,"dataGaName":458,"dataGaLocation":421,"icon":459},"back to pricing","GoBack",{"altText":423,"config":461},{"src":425,"dataGaName":426,"dataGaLocation":421},{"altText":423,"config":463},{"src":429,"dataGaName":426,"dataGaLocation":421},{"title":465,"button":466,"config":471},"Sieh dir an, wie agentische KI die Softwarebereitstellung transformiert",{"text":467,"config":468},"Für GitLab Transcend am 10. Juni anmelden",{"href":469,"dataGaName":470,"dataGaLocation":46},"/de-de/releases/whats-new/#sign-up","transcend event",{"layout":472,"icon":473,"disabled":474},"release","AiStar",false,{"data":476},{"text":477,"source":478,"edit":484,"contribute":489,"config":494,"items":499,"minimal":701},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":479,"config":480},"Quelltext der Seite anzeigen",{"href":481,"dataGaName":482,"dataGaLocation":483},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":485,"config":486},"Diese Seite bearbeiten",{"href":487,"dataGaName":488,"dataGaLocation":483},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":490,"config":491},"Beteilige dich",{"href":492,"dataGaName":493,"dataGaLocation":483},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":495,"facebook":496,"youtube":497,"linkedin":498},"https://x.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[500,545,597,639,666],{"title":194,"links":501,"subMenu":516},[502,506,511],{"text":503,"config":504},"Tarife anzeigen",{"href":196,"dataGaName":505,"dataGaLocation":483},"view plans",{"text":507,"config":508},"Vorteile von Premium",{"href":509,"dataGaName":510,"dataGaLocation":483},"/de-de/pricing/premium/","why premium",{"text":512,"config":513},"Vorteile von Ultimate",{"href":514,"dataGaName":515,"dataGaLocation":483},"/de-de/pricing/ultimate/","why ultimate",[517],{"title":363,"links":518},[519,521,523,525,530,535,540],{"text":53,"config":520},{"href":55,"dataGaName":56,"dataGaLocation":483},{"text":373,"config":522},{"href":375,"dataGaName":376,"dataGaLocation":483},{"text":378,"config":524},{"href":380,"dataGaName":381,"dataGaLocation":483},{"text":526,"config":527},"Status",{"href":528,"dataGaName":529,"dataGaLocation":483},"https://status.gitlab.com/","status",{"text":531,"config":532},"Nutzungsbedingungen",{"href":533,"dataGaName":534,"dataGaLocation":483},"/terms/","terms of use",{"text":536,"config":537},"Datenschutzerklärung",{"href":538,"dataGaName":539,"dataGaLocation":483},"/de-de/privacy/","privacy statement",{"text":541,"config":542},"Cookie-Einstellungen",{"dataGaName":543,"dataGaLocation":483,"id":544,"isOneTrustButton":15},"cookie preferences","ot-sdk-btn",{"title":93,"links":546,"subMenu":555},[547,551],{"text":548,"config":549},"DevSecOps-Plattform",{"href":75,"dataGaName":550,"dataGaLocation":483},"devsecops platform",{"text":552,"config":553},"KI-unterstützte Entwicklung",{"href":82,"dataGaName":554,"dataGaLocation":483},"ai-assisted development",[556],{"title":557,"links":558},"Themen",[559,562,567,572,577,582,587,592],{"text":22,"config":560},{"href":561,"dataGaName":38,"dataGaLocation":483},"/de-de/topics/ci-cd/",{"text":563,"config":564},"GitOps",{"href":565,"dataGaName":566,"dataGaLocation":483},"/de-de/topics/gitops/","gitops",{"text":568,"config":569},"DevOps",{"href":570,"dataGaName":571,"dataGaLocation":483},"/de-de/topics/devops/","devops",{"text":573,"config":574},"Versionskontrolle",{"href":575,"dataGaName":576,"dataGaLocation":483},"/de-de/topics/version-control/","version control",{"text":578,"config":579},"DevSecOps",{"href":580,"dataGaName":581,"dataGaLocation":483},"/de-de/topics/devsecops/","devsecops",{"text":583,"config":584},"Cloud-nativ",{"href":585,"dataGaName":586,"dataGaLocation":483},"/de-de/topics/cloud-native/","cloud native",{"text":588,"config":589},"KI für das Programmieren",{"href":590,"dataGaName":591,"dataGaLocation":483},"/de-de/topics/devops/ai-for-coding/","ai for coding",{"text":593,"config":594},"Agentische KI",{"href":595,"dataGaName":596,"dataGaLocation":483},"/de-de/topics/agentic-ai/","agentic ai",{"title":598,"links":599},"Lösungen",[600,603,605,610,614,617,620,623,625,627,629,634],{"text":137,"config":601},{"href":132,"dataGaName":602,"dataGaLocation":483},"Application Security Testing",{"text":124,"config":604},{"href":109,"dataGaName":110,"dataGaLocation":483},{"text":606,"config":607},"Agile Entwicklung",{"href":608,"dataGaName":609,"dataGaLocation":483},"/de-de/solutions/agile-delivery/","agile delivery",{"text":611,"config":612},"SCM",{"href":121,"dataGaName":613,"dataGaLocation":483},"source code management",{"text":22,"config":615},{"href":114,"dataGaName":616,"dataGaLocation":483},"continuous integration & delivery",{"text":163,"config":618},{"href":165,"dataGaName":619,"dataGaLocation":483},"value stream management",{"text":563,"config":621},{"href":622,"dataGaName":566,"dataGaLocation":483},"/de-de/solutions/gitops/",{"text":176,"config":624},{"href":179,"dataGaName":180,"dataGaLocation":483},{"text":182,"config":626},{"href":185,"dataGaName":186,"dataGaLocation":483},{"text":188,"config":628},{"href":191,"dataGaName":192,"dataGaLocation":483},{"text":630,"config":631},"Bildungswesen",{"href":632,"dataGaName":633,"dataGaLocation":483},"/de-de/solutions/education/","education",{"text":635,"config":636},"Finanzdienstleistungen",{"href":637,"dataGaName":638,"dataGaLocation":483},"/de-de/solutions/finance/","financial services",{"title":199,"links":640},[641,643,645,647,650,652,654,656,658,660,662,664],{"text":212,"config":642},{"href":214,"dataGaName":215,"dataGaLocation":483},{"text":217,"config":644},{"href":219,"dataGaName":220,"dataGaLocation":483},{"text":222,"config":646},{"href":224,"dataGaName":225,"dataGaLocation":483},{"text":227,"config":648},{"href":229,"dataGaName":649,"dataGaLocation":483},"docs",{"text":250,"config":651},{"href":252,"dataGaName":253,"dataGaLocation":483},{"text":245,"config":653},{"href":247,"dataGaName":248,"dataGaLocation":483},{"text":255,"config":655},{"href":257,"dataGaName":258,"dataGaLocation":483},{"text":263,"config":657},{"href":265,"dataGaName":266,"dataGaLocation":483},{"text":268,"config":659},{"href":270,"dataGaName":271,"dataGaLocation":483},{"text":273,"config":661},{"href":275,"dataGaName":276,"dataGaLocation":483},{"text":278,"config":663},{"href":280,"dataGaName":281,"dataGaLocation":483},{"text":283,"config":665},{"href":285,"dataGaName":286,"dataGaLocation":483},{"title":302,"links":667},[668,670,672,674,676,678,680,685,690,692,694,696],{"text":310,"config":669},{"href":312,"dataGaName":304,"dataGaLocation":483},{"text":315,"config":671},{"href":317,"dataGaName":318,"dataGaLocation":483},{"text":323,"config":673},{"href":325,"dataGaName":326,"dataGaLocation":483},{"text":328,"config":675},{"href":330,"dataGaName":331,"dataGaLocation":483},{"text":333,"config":677},{"href":335,"dataGaName":336,"dataGaLocation":483},{"text":338,"config":679},{"href":340,"dataGaName":341,"dataGaLocation":483},{"text":681,"config":682},"Nachhaltigkeit",{"href":683,"dataGaName":684,"dataGaLocation":483},"/sustainability/","Sustainability",{"text":686,"config":687},"Vielfalt, Inklusion und Zugehörigkeit",{"href":688,"dataGaName":689,"dataGaLocation":483},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":343,"config":691},{"href":345,"dataGaName":346,"dataGaLocation":483},{"text":353,"config":693},{"href":355,"dataGaName":356,"dataGaLocation":483},{"text":358,"config":695},{"href":360,"dataGaName":361,"dataGaLocation":483},{"text":697,"config":698},"Transparenzerklärung zu moderner Sklaverei",{"href":699,"dataGaName":700,"dataGaLocation":483},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":702},[703,705,708],{"text":531,"config":704},{"href":533,"dataGaName":534,"dataGaLocation":483},{"text":706,"config":707},"Cookies",{"dataGaName":543,"dataGaLocation":483,"id":544,"isOneTrustButton":15},{"text":536,"config":709},{"href":538,"dataGaName":539,"dataGaLocation":483},[711,724],{"id":712,"title":10,"body":28,"config":713,"content":715,"description":28,"extension":27,"meta":719,"navigation":15,"path":720,"seo":721,"stem":722,"__hash__":723},"blogAuthors/en-us/blog/authors/evgeny-rudinsky.yml",{"template":714},"BlogAuthor",{"name":10,"config":716},{"ctfId":717,"headshot":718},"erudinsky","https://res.cloudinary.com/about-gitlab-com/image/upload/v1761069206/yppyhhdmdrw4u1nz7het.jpg",{},"/en-us/blog/authors/evgeny-rudinsky",{},"en-us/blog/authors/evgeny-rudinsky","OtgSXy1cMxWS2khqtu-PdUFWhG9Eck_jUQfH5JUwzWM",{"id":725,"title":11,"body":28,"config":726,"content":727,"description":28,"extension":27,"meta":731,"navigation":15,"path":732,"seo":733,"stem":734,"__hash__":735},"blogAuthors/en-us/blog/authors/michael-leopard.yml",{"template":714},{"name":11,"config":728},{"ctfId":729,"headshot":730},"leopardm","https://res.cloudinary.com/about-gitlab-com/image/upload/v1761069206/tjpzzunnzhen77u6tf5s.jpg",{},"/en-us/blog/authors/michael-leopard",{},"en-us/blog/authors/michael-leopard","Yavr1VB0sBdgyY2GfU8OYVm_Byiv9EzriWbuDzn6Al4",[737,750,764],{"content":738,"config":748},{"title":739,"description":740,"authors":741,"heroImage":743,"date":744,"body":745,"category":13,"tags":746},"CI/CD-Observability im Unternehmensmaßstab aufbauen","Dieser Praxisleitfaden zu GitLab Pipeline Analytics hilft Self-Managed-Nutzern, mit Prometheus und Grafana operationale Insights zu gewinnen.",[742],"Paul Meresanu","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28","CI/CD-Optimierung beginnt mit Transparenz. Eine erfolgreiche DevOps-Plattform\nim Unternehmensmaßstab umfasst das Verständnis von Pipeline-Performance,\nJob-Ausführungsmustern und quantifizierbaren operationalen Insights – insbesondere\nfür Unternehmen, die GitLab Self-Managed betreiben.\n\nUm GitLab-Kund(inn)en dabei zu helfen, den vollen Nutzen ihrer Plattform\nauszuschöpfen, haben wir die GitLab CI/CD Observability-Lösung als Teil unseres\nPlatform Excellence-Programms entwickelt. Sie verwandelt rohe Pipeline-Metriken\nin handlungsrelevante operationale Erkenntnisse.\n\nEin führendes Finanzdienstleistungsunternehmen hat gemeinsam mit GitLabs Customer\nSuccess Architect Transparenz über seine GitLab Self-Managed-Deployments\ngewonnen. Gemeinsam haben wir eine containerisierte Observability-Lösung\nimplementiert, die den Open-Source-gitlab-ci-pipelines-exporter mit\nunternehmensgerechter Prometheus- und Grafana-Infrastruktur kombiniert.\n\nIn diesem Artikel werden die Herausforderungen beim Pipeline-Management im\nUnternehmensmaßstab erläutert – und wie GitLab CI/CD Observability diese mit\neiner praxisnahen End-to-End-Implementierung adressiert.\n\n\n## Die Herausforderung: CI/CD-Performance messen\n\nVor der Implementierung einer Observability-Lösung sollte die\nMessdimension klar definiert sein:\n\n* **Welche Metriken sind relevant?** Pipeline-Dauer, Job-Erfolgsraten,\n  Queue-Zeiten, Runner-Auslastung\n* **Wer braucht Transparenz?** Entwickler(innen), DevOps-Engineers,\n  Plattformteams, Führungsebene\n* **Welche Entscheidungen werden damit getroffen?** Infrastrukturinvestitionen,\n  Engpass-Behebung, Kapazitätsplanung\n\n\n## Lösungsarchitektur: Ein vollständiges Dashboard-Set für Observability\n\nNach dem Deployment stellt der Observability-Stack ein Set von\nGrafana-Dashboards bereit, das Echtzeit- und historische Transparenz über die\nCI/CD-Plattform bietet. Ein typisches Deployment umfasst:\n\n* **Pipeline Overview Dashboard:** Eine übergeordnete Ansicht mit Gesamtzahl\n  der Pipeline-Läufe, Erfolgs-/Fehlerquoten über die Zeit (als gestapelte\n  Balken- oder Zeitreihencharts) und Trends bei der durchschnittlichen\n  Pipeline-Dauer. Panels verwenden farbcodierte Statusindikatoren (Grün für\n  Erfolg, Rot für Fehler, Gelb für Abbruch), damit Plattformteams\n  Verschlechterungen auf einen Blick erkennen.\n* **Job Performance Dashboard:** Drill-down-Panels mit Verteilungen der\n  einzelnen Job-Dauern (Histogramm), den 10 langsamsten Jobs nach\n  Durchschnittsdauer und Job-Fehler-Heatmaps nach Projekt und Stage. Hier\n  identifizieren Teams konkrete Engpass-Jobs, die sich zu optimieren lohnen.\n* **Runner & Infrastructure Dashboard:** Kombiniert Node-Exporter-Host-Metriken\n  (CPU, Arbeitsspeicher, Disk) mit Pipeline-Queue-Zeit-Daten, um\n  Infrastruktur-Sättigung mit Pipeline-Wartezeiten zu korrelieren. Nützlich\n  für Kapazitätsplanungsentscheidungen wie die Skalierung von Runner-Pools oder\n  das Upgrade von Instanzgrößen.\n* **Deployment Frequency Dashboard:** Verfolgt Deployment-Anzahl und\n  -Dauer über die Zeit pro Umgebung, abgestimmt auf DORA-Metriken. Hilft\n  der Engineering-Führungsebene, Lieferdurchsatz und Environment-Drift\n  (Commits hinter main) zu bewerten.\n\nJedes Dashboard wird automatisch über Grafanas dateibasiertes Provisioning\nbereitgestellt, sodass es konsistent über alle Umgebungen hinweg deployed wird.\nDie Dashboards lassen sich über Grafana-Variablen weiter anpassen, um nach\nProjekt, Ref/Branch oder Zeitraum zu filtern.\n\n![Lösungsarchitektur](https://res.cloudinary.com/about-gitlab-com/image/upload/v1777382608/Blog/Imported/blog-building-ci-cd-observability-stack-for-gitlab-self-managed/image1.png)\n\nDie Lösung benötigt zwei Exporter:\n\n* **Pipeline Exporter:** Erfasst CI/CD-Metriken über die GitLab API\n  (Pipeline-Dauer, Job-Status, Deployments)\n* **Node Exporter:** Erfasst Host-Metriken (CPU, Arbeitsspeicher, Disk)\n  für die Infrastruktur-Korrelation\n\n**Voraussetzungen:**\n\n* GitLab Self-Managed Version 18.1+\n* **Container-Orchestrierungsplattform:** Ein Kubernetes-Cluster (empfohlen\n  für Unternehmens-Deployments) oder eine Container-Runtime wie Docker/Podman\n  für kleinere Umgebungen oder Proof-of-Concept-Deployments. Die primäre\n  Deployment-Anleitung unten zielt auf Kubernetes; eine Docker-Compose-Alternative\n  ist im Anhang für lokales Testen und Evaluation verfügbar\n* GitLab Personal Access Token (Scope **read_api**)\n\nDie vollständige Implementierungsanleitung mit allen Kubernetes-Manifesten\nfolgt direkt im Anschluss.\n\n\n## Kubernetes-Deployment (empfohlen)\n\nFür Unternehmensumgebungen wird jede Komponente als separates Deployment in\neinem dedizierten Namespace deployed. Dieser Ansatz integriert sich in\nbestehende Cluster-Infrastruktur, Secrets-Management und Network-Policies.\n\n### 1. Namespace und Secret erstellen\n\n```bash\nkubectl create namespace gitlab-observability\n\n# GitLab-Token-Secret erstellen (siehe Abschnitt Secrets-Management\n# für unternehmensgerechte Ansätze mit externen Secret-Operatoren)\nkubectl create secret generic gitlab-token \\\n  --from-literal=token=glpat-xxxxxxxxxxxx \\\n  -n gitlab-observability\n```\n\n### 2. Pipeline Exporter deployen\n\n```yaml\n# exporter-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: gitlab-ci-pipelines-exporter\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: gitlab-ci-pipelines-exporter\n  template:\n    metadata:\n      labels:\n        app: gitlab-ci-pipelines-exporter\n    spec:\n      containers:\n        - name: exporter\n          image: mvisonneau/gitlab-ci-pipelines-exporter:latest\n          ports:\n            - containerPort: 8080\n          env:\n            - name: GCPE_GITLAB_TOKEN\n              valueFrom:\n                secretKeyRef:\n                  name: gitlab-token\n                  key: token\n            - name: GCPE_CONFIG\n              value: /etc/gcpe/config.yml\n          volumeMounts:\n            - name: config\n              mountPath: /etc/gcpe\n      volumes:\n        - name: config\n          configMap:\n            name: gcpe-config\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: gitlab-ci-pipelines-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: gitlab-ci-pipelines-exporter\n  ports:\n    - port: 8080\n      targetPort: 8080\n```\n\n### 3. Node Exporter deployen (DaemonSet)\n\n```yaml\n# node-exporter-daemonset.yaml\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: node-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    matchLabels:\n      app: node-exporter\n  template:\n    metadata:\n      labels:\n        app: node-exporter\n    spec:\n      containers:\n        - name: node-exporter\n          image: prom/node-exporter:latest\n          ports:\n            - containerPort: 9100\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: node-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: node-exporter\n  ports:\n    - port: 9100\n      targetPort: 9100\n```\n\n### 4. Prometheus deployen\n\n```yaml\n# prometheus-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: prometheus\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: prometheus\n  template:\n    metadata:\n      labels:\n        app: prometheus\n    spec:\n      containers:\n        - name: prometheus\n          image: prom/prometheus:latest\n          ports:\n            - containerPort: 9090\n          volumeMounts:\n            - name: config\n              mountPath: /etc/prometheus\n      volumes:\n        - name: config\n          configMap:\n            name: prometheus-config\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: prometheus\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: prometheus\n  ports:\n    - port: 9090\n      targetPort: 9090\n```\n\n### 5. Grafana deployen\n\nDas folgende Grafana-Deployment startet mit deaktivierter Authentifizierung\n(`GF_AUTH_ANONYMOUS_ENABLED: true`) für den einfachen Einstieg.\n\n**Diese Einstellung erlaubt jedem mit Netzwerkzugang, alle Dashboards ohne\nAnmeldung einzusehen.** Für Produktions-Deployments diese Variable entfernen\noder auf false setzen und einen geeigneten Authentifizierungs-Provider\n(LDAP, SAML/SSO oder OAuth) konfigurieren, um den Zugriff auf autorisierte\nNutzende zu beschränken.\n\n```yaml\n# grafana-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: grafana\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: grafana\n  template:\n    metadata:\n      labels:\n        app: grafana\n    spec:\n      containers:\n        - name: grafana\n          image: grafana/grafana:10.0.0\n          ports:\n            - containerPort: 3000\n          env:\n            # Für Produktion ENTFERNEN oder auf 'false' setzen.\n            # Bei 'true' können alle Nutzenden mit Netzwerkzugang\n            # Dashboards ohne Authentifizierung einsehen.\n            - name: GF_AUTH_ANONYMOUS_ENABLED\n              value: 'true'\n          volumeMounts:\n            - name: dashboards-provider\n              mountPath: /etc/grafana/provisioning/dashboards\n            - name: datasources\n              mountPath: /etc/grafana/provisioning/datasources\n            - name: dashboards\n              mountPath: /var/lib/grafana/dashboards\n      volumes:\n        - name: dashboards-provider\n          configMap:\n            name: grafana-dashboards-provider\n        - name: datasources\n          configMap:\n            name: grafana-datasources\n        - name: dashboards\n          configMap:\n            name: grafana-dashboards\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: grafana\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: grafana\n  ports:\n    - port: 3000\n      targetPort: 3000\n```\n\n### 6. Network Policy setzen\n\nDen Inter-Pod-Traffic auf die erforderlichen Kommunikationspfade beschränken:\n\n```yaml\n# network-policy.yaml\napiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: observability-policy\n  namespace: gitlab-observability\nspec:\n  podSelector: {}\n  policyTypes:\n    - Ingress\n  ingress:\n    # Prometheus scrapt Exporter und Node-Exporter\n    - from:\n        - podSelector:\n            matchLabels:\n              app: prometheus\n      ports:\n        - port: 8080\n        - port: 9100\n    # Grafana fragt Prometheus ab\n    - from:\n        - podSelector:\n            matchLabels:\n              app: grafana\n      ports:\n        - port: 9090\n```\n\n### 7. Validieren\n\n```bash\nkubectl get pods -n gitlab-observability\nkubectl port-forward svc/grafana 3000:3000 -n gitlab-observability\ncurl http://localhost:3000/api/health\n```\n\n\n## Konfigurationsreferenz\n\n### Exporter-Konfiguration\n\n```yaml\n# gitlab-ci-pipelines-exporter.yml (ConfigMap: gcpe-config)\nlog:\n  level: info\ngitlab:\n  url: https://gitlab.your-domain.com\n  maximum_requests_per_second: 10\nproject_defaults:\n  pull:\n    pipeline:\n      jobs:\n        enabled: true\nwildcards:\n  - owner:\n      name: your-group-name\n      kind: group\n    archived: false\n```\n\n### Prometheus-Konfiguration\n\n```yaml\n# prometheus.yml (ConfigMap: prometheus-config)\nglobal:\n  scrape_interval: 15s\nscrape_configs:\n  - job_name: 'gitlab-ci-pipelines-exporter'\n    static_configs:\n      - targets: ['gitlab-ci-pipelines-exporter:8080']\n  - job_name: 'node-exporter'\n    static_configs:\n      - targets: ['node-exporter:9100']\n```\n\n### Grafana-Datenquellen\n\n```yaml\n# datasources.yml (ConfigMap: grafana-datasources)\napiVersion: 1\ndatasources:\n  - name: Prometheus\n    type: prometheus\n    access: proxy\n    url: http://prometheus:9090\n    isDefault: true\n# dashboards.yml (ConfigMap: grafana-dashboards-provider)\napiVersion: 1\nproviders:\n  - name: 'default'\n    folder: 'GitLab CI/CD'\n    type: file\n    options:\n      path: /var/lib/grafana/dashboards\n```\n\n\n## Wichtige Metriken\n\n### Pipeline-Exporter-Metriken\n\n| Metrik | Beschreibung |\n| :---- | :---- |\n| `gitlab_ci_pipeline_duration_seconds` | Pipeline-Ausführungszeit |\n| `gitlab_ci_pipeline_status` | Pipeline-Erfolg/-Fehler nach Projekt |\n| `gitlab_ci_pipeline_job_duration_seconds` | Einzelne Job-Ausführungszeit |\n| `gitlab_ci_pipeline_job_status` | Job-Erfolgs-/-Fehlerstatus |\n| `gitlab_ci_pipeline_job_artifact_size_bytes` | Artifact-Speicherverbrauch |\n| `gitlab_ci_pipeline_coverage` | Code-Coverage-Prozentsatz |\n| `gitlab_ci_environment_deployment_count` | Deployment-Häufigkeit |\n| `gitlab_ci_environment_deployment_duration_seconds` | Deployment-Ausführungszeit |\n| `gitlab_ci_environment_behind_commits_count` | Environment-Drift gegenüber main |\n\n### Node-Exporter-Metriken\n\n| Metrik | Beschreibung |\n| :---- | :---- |\n| `node_cpu_seconds_total` | CPU-Auslastung |\n| `node_memory_MemAvailable_bytes` | Verfügbarer Arbeitsspeicher |\n| `node_filesystem_avail_bytes` | Verfügbarer Festplattenspeicher |\n| `node_load1` | 1-Minuten-Lastdurchschnitt |\n\n\n## Fehlerbehebung\n\n### Grafana-Plugin-Installation in Air-gapped-Umgebungen\n\nFür Offline-Umgebungen Plugins manuell installieren. Beispiel für Kubernetes:\n\n```bash\n# Plugin-ZIP in den Grafana-Pod kopieren\nkubectl cp grafana-polystat-panel-2.1.16.zip \\\n  gitlab-observability/grafana-\u003Cpod-id>:/tmp/\n# Plugin entpacken\nkubectl exec -it -n gitlab-observability deploy/grafana -- \\\n  sh -c \"unzip /tmp/grafana-polystat-panel-2.1.16.zip -d /var/lib/grafana/plugins/\"\n# Grafana-Pod neu starten\nkubectl rollout restart deployment/grafana -n gitlab-observability\n# Installation prüfen\nkubectl exec -it -n gitlab-observability deploy/grafana -- \\\n  ls -al /var/lib/grafana/plugins/\n```\n\n\n## Unternehmensaspekte\n\nFür regulierte Branchen gilt:\n\n* **Token-Sicherheit:** GitLab Personal Access Tokens in einem dedizierten\n  Secrets-Manager speichern, nicht hartcodiert in ConfigMaps. Token-Rotation\n  durchsetzen und den Scope auf **read\\_api** beschränken.\n* **Netzwerksegmentierung:** Hinter einem Reverse Proxy mit TLS-Terminierung\n  deployen. In Kubernetes einen Ingress-Controller mit automatisierter\n  Zertifikatsbereitstellung verwenden.\n* **Authentifizierung:** Grafana mit dem Identity Provider der Organisation\n  konfigurieren (SAML, LDAP oder OAuth/OIDC), um rollenbasierte\n  Zugriffskontrolle auf Dashboards durchzusetzen.\n\n\n## Warum GitLab?\n\nGitLabs API-First-Design ermöglicht individuelle Observability-Lösungen, die\nnative Funktionen wie Value Stream Analytics und DORA-Metriken ergänzen. Die\noffene Architektur erlaubt es Unternehmen, bewährte Open-Source-Werkzeuge –\nwie den gitlab-ci-pipelines-exporter – direkt in bestehende\nUnternehmensinfrastruktur zu integrieren, ohne etablierte Workflows zu\nunterbrechen.\n\nMit wachsender Observability-Reife bieten GitLabs eingebaute\nObservability-Funktionen einen natürlichen nächsten Schritt – tiefere,\nintegrierte Transparenz ohne zusätzliche Werkzeuge. Mehr zu den nativen\nPlattformfunktionen unter\n[GitLab Observability](https://docs.gitlab.com/operations/observability/observability/).\n",[22,747,20],"product",{"featured":474,"template":16,"slug":749},"how-to-build-ci-cd-observability-at-scale",{"content":751,"config":762},{"body":752,"title":753,"description":754,"authors":755,"heroImage":757,"date":758,"category":13,"tags":759},"## Abschnitt 1: Das Modell verstehen\n*Für Engineering-Leads und Entscheidungsträger: Konzept, Anwendungsfälle und Architekturprinzipien. Konfigurationsdetails folgen in Abschnitt 2.*\n\nDie meisten CI/CD-Werkzeuge können einen Build ausführen und ein Deployment anstoßen. Der Unterschied zeigt sich erst dann, wenn die Delivery-Anforderungen komplexer werden: ein Monorepo mit einem Dutzend Services, Microservices über mehrere Repositories verteilt, Deployments in Dutzende von Umgebungen gleichzeitig – oder ein Platform-Team, das organisationsweite Standards durchsetzen will, ohne dabei zum Engpass zu werden.\n\nGitLabs Pipeline-Modell wurde für genau diese Komplexität entwickelt. Parent-Child-Pipelines, DAG-Execution, dynamische Pipeline-Generierung, Multi-Project-Trigger, Merge-Request-Pipelines mit Merged-Results-Verarbeitung und CI/CD Components lösen jeweils eine eigene Klasse von Problemen. Da sich diese Bausteine kombinieren lassen, erschließt das vollständige Modell mehr als nur kürzere Pipeline-Laufzeiten.\n\nDieser Artikel beschreibt die fünf Muster, bei denen das Modell seine Stärken deutlich zeigt – jeweils zugeordnet zu einem konkreten Engineering-Szenario. Konfigurationen und Implementierungsdetails folgen in Abschnitt 2.\n\n### 1. Monorepos: Parent-Child-Pipelines und DAG-Execution\n\n**Das Problem:** Ein Monorepo enthält Frontend, Backend und Dokumentation. Jeder Commit löst einen vollständigen Rebuild aller Komponenten aus – auch wenn sich nur eine README-Datei geändert hat.\n\nGitLab kombiniert zwei sich ergänzende Mechanismen: [Parent-Child-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#parent-child-pipelines) ermöglichen es einer übergeordneten Pipeline, isolierte Child-Pipelines zu starten. [DAG-Execution via `needs`](https://docs.gitlab.com/ci/yaml/#needs) bricht die starre Stage-Reihenfolge auf und startet Jobs, sobald ihre Abhängigkeiten abgeschlossen sind – nicht erst, wenn alle Jobs einer Stage fertig sind.\n\nEine Parent-Pipeline erkennt, welche Teile des Repos sich geändert haben, und löst ausschließlich die betroffenen Child-Pipelines aus. Jeder Service verwaltet seine eigene Pipeline-Konfiguration; Änderungen in einem Service können keine anderen beeinflussen. Damit bleibt die Komplexität beherrschbar, während das Repository und das Team wachsen.\n\nEinen technischen Aspekt gilt es dabei zu kennen: Wenn mehrere Dateien an einen einzelnen `trigger: include:`-Block übergeben werden, fusioniert GitLab sie zu einer einzigen Child-Pipeline-Konfiguration. Jobs aus diesen Dateien teilen denselben Pipeline-Kontext und können sich gegenseitig per `needs:` referenzieren – das ist die Voraussetzung für die DAG-Optimierung. Werden die Dateien stattdessen auf separate Trigger-Jobs aufgeteilt, entsteht jeweils eine isolierte Pipeline, und dateiübergreifende `needs:`-Referenzen funktionieren nicht.\n\nIn großen Monorepos lassen sich Pipeline-Laufzeiten durch DAG-Execution deutlich reduzieren, da Jobs nicht mehr auf unabhängige Arbeitsschritte in derselben Stage warten.\n\n### 2. Microservices: Cross-Repo-Pipelines über mehrere Projekte\n\n**Das Problem:** Frontend und Backend leben in separaten Repositories. Wenn das Frontend-Team eine Änderung ausliefert, ist nicht erkennbar, ob sie die Backend-Integration beeinträchtigt – und umgekehrt.\n\n[Multi-Project-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#multi-project-pipelines) ermöglichen es, aus einem Projekt heraus eine Pipeline in einem anderen Projekt auszulösen und auf das Ergebnis zu warten. Das auslösende Projekt sieht die verknüpfte Downstream-Pipeline direkt in seiner eigenen Pipeline-Ansicht.\n\nIn der Praxis erstellt die Frontend-Pipeline ein API-Contract-Artifact und veröffentlicht es, bevor die Backend-Pipeline ausgelöst wird. Das Backend ruft dieses Artifact über die [Jobs API](https://docs.gitlab.com/api/jobs/#download-a-single-artifact-file-from-specific-tag-or-branch) ab und validiert es, bevor weitere Schritte erlaubt sind. Wird eine Breaking Change erkannt, schlägt die Backend-Pipeline fehl – und mit ihr die Frontend-Pipeline. Probleme, die bisher erst in der Produktion sichtbar wurden, werden damit im Pipeline-Prozess abgefangen. Die Abhängigkeit zwischen Services wird sichtbar, nachvollziehbar und aktiv verwaltbar.\n\n![Cross-project pipelines](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738762/Blog/Imported/hackathon-fake-blog-post-s/image4_h6mfsb.png \"Cross-project pipelines\") *Cross-project pipelines*\n\n### 3. Multi-Tenant/Matrix-Deployments: Dynamische Child-Pipelines\n\n**Das Problem:** Dieselbe Anwendung wird in 15 Kundenumgebungen, drei Cloud-Regionen oder den Stages Dev/Staging/Prod deployed. Manuelle Anpassungen je Umgebung führen zu Konfigurationsdrift. Eine separate Pipeline pro Umgebung ist von Anfang an nicht wartbar.\n\n[Dynamische Child-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#dynamic-child-pipelines) generieren die Pipeline-Struktur zur Laufzeit. Ein Job führt ein Skript aus, das eine YAML-Datei erzeugt – und diese YAML-Datei wird zur Pipeline für den nächsten Schritt. Die Pipeline-Struktur selbst wird damit zu Daten.\n\nDas Generierungsskript iteriert über eine `ENVIRONMENTS`-Variable, statt jede Umgebung fest zu kodieren. Eine neue Umgebung lässt sich durch Anpassen der Variable hinzufügen – ohne Änderungen an der Pipeline-Konfiguration selbst. Trigger-Jobs erben mit `extends:` eine gemeinsame Template-Konfiguration, sodass `strategy: depend` einmal definiert und nicht für jeden Trigger-Job wiederholt wird. Ein `when: manual`-Gate für das Produktions-Deployment ist direkt in den Pipeline-Graph integriert.\n\nPlatform-Teams nutzen dieses Muster, um Dutzende von Umgebungen zu verwalten, ohne Pipeline-Logik zu duplizieren.\n\n![Dynamic pipeline](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738765/Blog/Imported/hackathon-fake-blog-post-s/image7_wr0kx2.png \"Dynamic pipeline\")\n\n### 4. MR-First-Delivery: Merge-Request-Pipelines, Merged-Results und Workflow-Routing\n\n**Das Problem:** Die Pipeline läuft bei jedem Push auf jeden Branch. Aufwändige Tests werden auf Feature-Branches ausgeführt, die nie gemergt werden. Gleichzeitig gibt es keine Garantie, dass das Getestete dem entspricht, was nach dem Merge auf `main` tatsächlich landet.\n\nGitLab kombiniert drei ineinandergreifende Mechanismen: [Merge-Request-Pipelines](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/) laufen ausschließlich dann, wenn ein Merge Request existiert – nicht bei jedem Branch-Push. Allein dadurch entfällt ein erheblicher Anteil unnötiger Compute-Ausführungen. [Merged-Results-Pipelines](https://docs.gitlab.com/ci/pipelines/merged_results_pipelines/) gehen einen Schritt weiter: GitLab erstellt einen temporären Merge-Commit aus dem Branch und dem aktuellen Ziel-Branch und führt die Pipeline dagegen aus. Getestet wird damit das tatsächliche Ergebnis des Merges – nicht der Branch in Isolation. [Workflow-Rules](https://docs.gitlab.com/ci/yaml/workflow/) definieren schließlich, welcher Pipeline-Typ unter welchen Bedingungen ausgeführt wird. Die `$CI_OPEN_MERGE_REQUESTS`-Guard verhindert dabei, dass für einen Branch mit offenem MR doppelte Pipelines ausgelöst werden.\n\nDas Ergebnis ist ein Pipeline-Verhalten, das sich je nach Kontext unterscheidet: Ein Push auf einen Feature-Branch ohne offenen MR führt nur Lint und Unit-Tests aus. Sobald ein MR geöffnet wird, wechseln die Workflow-Rules auf eine MR-Pipeline mit der vollständigen Test-Suite gegen das Merged-Result. Ein Merge auf `main` stellt ein manuelles Produktions-Deployment in die Warteschlange. Der Nightly-Scan läuft einmalig als geplante Pipeline – nicht bei jedem Commit.\n\nMerged-Results-Pipelines fangen dabei die Klasse von Fehlern ab, die erst nach einem Merge sichtbar werden – bevor sie `main` erreichen.\n\n### 5. Governed Pipelines: CI/CD Components\n\n**Das Problem:** Das Platform-Team hat den richtigen Weg für Build, Test und Deploy definiert. Jedes Anwendungsteam pflegt jedoch eine eigene `.gitlab-ci.yml` mit subtilen Abweichungen. Security-Scanning wird übersprungen. Deployment-Standards driften. Audits werden aufwändig.\n\n[CI/CD Components](https://docs.gitlab.com/ci/components/) ermöglichen es Platform-Teams, versionierte, wiederverwendbare Pipeline-Bausteine zu veröffentlichen. Anwendungsteams binden sie mit einer einzigen `include:`-Zeile ein – kein Copy-Paste, kein Drift. Components sind über den [CI/CD Catalog](https://docs.gitlab.com/ci/components/#cicd-catalog) auffindbar, sodass Teams bewährte Bausteine finden und übernehmen können, ohne das Platform-Team direkt einschalten zu müssen.\n\nDrei Zeilen `include:` ersetzen hunderte von duplizierten YAML-Zeilen. Das Platform-Team kann einen Security-Fix in einer neuen Komponentenversion veröffentlichen – Teams steigen auf ihrem eigenen Zeitplan um, oder das Platform-Team fixiert alle auf eine Mindestversion. In beiden Fällen propagiert eine Änderung organisationsweit, statt repo-für-repo angewendet zu werden.\n\nKombiniert mit [Resource Groups](https://docs.gitlab.com/ci/resource_groups/) zur Vermeidung konkurrierender Deployments und [Protected Environments](https://docs.gitlab.com/ci/environments/protected_environments/) für Freigabe-Gates entsteht eine governed Delivery-Plattform, auf der **Compliance der Standard ist, nicht die Ausnahme**. Platform-Teams setzen Vorgaben durch, ohne zum Engpass zu werden.\n\n![Component pipeline (imported jobs)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738776/Blog/Imported/hackathon-fake-blog-post-s/image2_pizuxd.png \"Component pipeline (imported jobs)\")\n\n## Das Modell als Ganzes\n\nKeines dieser Muster existiert isoliert. Der Wert von GitLabs Pipeline-Modell liegt in der Kombinierbarkeit seiner Bausteine:\n\n- Ein Monorepo nutzt Parent-Child-Pipelines, und jede Child-Pipeline nutzt DAG-Execution.\n- Eine Microservices-Plattform nutzt Multi-Project-Pipelines, und jedes Projekt nutzt MR-Pipelines mit Merged-Results.\n- Eine governed Plattform nutzt CI/CD Components, um die obigen Muster organisationsweit zu standardisieren.\n\nDie meisten Teams entdecken eines dieser Muster, wenn sie auf ein konkretes Problem stoßen. Teams, die das vollständige Modell verstehen, entwickeln daraus eine Delivery-Infrastruktur, die tatsächlich abbildet, wie ihre Engineering-Organisation arbeitet – und mit ihr wächst.\n\n## Weitere Muster\n\nDas Pipeline-Modell geht über die fünf vorgestellten Muster hinaus:\n\n- [Review Apps mit dynamischen Umgebungen](https://docs.gitlab.com/ci/environments/) erstellen für jeden Feature-Branch eine Live-Vorschau und räumen sie automatisch auf, wenn der MR geschlossen wird.\n- [Caching- und Artifact-Strategien](https://docs.gitlab.com/ci/caching/) sind nach der strukturellen Arbeit häufig der direkteste Weg zur weiteren Laufzeitoptimierung – ohne die Pipeline-Struktur zu verändern.\n- [Geplante und API-ausgelöste Pipelines](https://docs.gitlab.com/ci/pipelines/schedules/) eignen sich für Workloads, die nicht bei jedem Code-Push laufen sollten: Nightly-Security-Scans, Compliance-Reports und Release-Automatisierung lassen sich als geplante oder [API-ausgelöste](https://docs.gitlab.com/ci/triggers/) Pipelines mit `$CI_PIPELINE_SOURCE`-Routing modellieren.\n\n> [GitLab Ultimate kostenlos testen](https://about.gitlab.com/de-de/free-trial/) und Pipeline-Logik ab heute einsetzen.\n\n## Für deutsche Unternehmen: Regulatorischer Kontext\n\nTeams, die Pipeline-Governance nach Muster 5 einführen, adressieren dabei möglicherweise auch Anforderungen, die regulatorische Frameworks an sichere Softwareentwicklungsprozesse stellen.\n\nCI/CD Components mit erzwungenen Security-Gates könnten Anforderungen an sichere Entwicklungsprozesse betreffen – beispielsweise in Bereichen, die Frameworks wie NIS2, ISO 27001 oder BSI IT-Grundschutz an den Software-Entwicklungslebenszyklus adressieren. Protected Environments und Resource Groups betreffen ähnliche Themen im Bereich Änderungskontrolle und Umgebungstrennung, wie sie in Governance-Frameworks typischerweise explizit formuliert sind.\n\nMulti-Project-Pipelines mit API-Contract-Validierung (Muster 2) schaffen Sichtbarkeit über Service-Abhängigkeiten hinweg – ein Aspekt, den Frameworks zur Lieferkettensicherheit adressieren.\n\nMerged-Results-Pipelines (Muster 4) dokumentieren automatisch, dass das tatsächliche Merge-Ergebnis getestet wurde, nicht nur der Feature-Branch in Isolation. Dies könnte Anforderungen an nachvollziehbare Änderungsprozesse betreffen, wie sie in Change-Management-Kontrollen verschiedener Sicherheitsframeworks formuliert sind.\n\nFür konkrete Compliance-Anforderungen im eigenen regulatorischen Umfeld empfiehlt sich Rücksprache mit entsprechender Fachberatung.\n\n## Abschnitt 2: Konfiguration und Implementierung\n\n*Für Entwicklungsteams und DevOps-Praktiker: ausgewählte Konfigurationsbeispiele zu den Mustern 1, 4 und 5. Für vollständige Konfigurationen aller Muster: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/).*\n\nDie folgenden Konfigurationen sind illustrativ aufgebaut. Die Skripte verwenden `echo`-Befehle, um das Wesentliche sichtbar zu halten. Für den produktiven Einsatz werden die `echo`-Befehle durch die tatsächlichen Build-, Test- und Deploy-Schritte ersetzt.\n\n### Muster 1: Parent-Child-Pipelines und DAG-Execution\n\nEine Parent-Pipeline erkennt Änderungen und löst nur die betroffenen Child-Pipelines aus:\n\n```yaml # .gitlab-ci.yml stages:\n  - trigger\n\ntrigger-services:\n  stage: trigger\n  trigger:\n    include:\n      - local: '.gitlab/ci/api-service.yml'\n      - local: '.gitlab/ci/web-service.yml'\n      - local: '.gitlab/ci/worker-service.yml'\n    strategy: depend\n```\n\nInnerhalb der Child-Pipeline ermöglicht `needs:` DAG-Execution – der Test startet, sobald der Build abgeschlossen ist, ohne auf andere Jobs in derselben Stage zu warten:\n\n```yaml # .gitlab/ci/api-service.yml stages:\n  - build\n  - test\n\nbuild-api:\n  stage: build\n  script:\n    - echo \"Building API service\"\n\ntest-api:\n  stage: test\n  needs: [build-api]\n  script:\n    - echo \"Running API tests\"\n```\n\n![Local downstream pipelines](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738759/Blog/Imported/hackathon-fake-blog-post-s/image3_vwj3rz.png \"Local downstream pipelines\")\n\n### Muster 4: MR-First-Delivery\n\nWorkflow-Rules, MR-Pipelines und Merged-Results zusammen ergeben ein kontextabhängiges Pipeline-Verhalten:\n\n```yaml # .gitlab-ci.yml workflow:\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS\n      when: never\n    - if: $CI_COMMIT_BRANCH\n    - if: $CI_PIPELINE_SOURCE == \"schedule\"\n\nstages:\n  - fast-checks\n  - expensive-tests\n  - deploy\n\nlint-code:\n  stage: fast-checks\n  script:\n    - echo \"Running linter\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"push\"\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nunit-tests:\n  stage: fast-checks\n  script:\n    - echo \"Running unit tests\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"push\"\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nintegration-tests:\n  stage: expensive-tests\n  script:\n    - echo \"Running integration tests (15 min)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\ne2e-tests:\n  stage: expensive-tests\n  script:\n    - echo \"Running E2E tests (30 min)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nnightly-comprehensive-scan:\n  stage: expensive-tests\n  script:\n    - echo \"Running full nightly suite (2 hours)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"schedule\"\n\ndeploy-production:\n  stage: deploy\n  script:\n    - echo \"Deploying to production\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n      when: manual\n```\n\n![Conditional pipelines (within a branch with no MR)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738768/Blog/Imported/hackathon-fake-blog-post-s/image6_dnfcny.png \"Conditional pipelines (within a branch with no MR)\")\n\n![Conditional pipelines (within an MR)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738772/Blog/Imported/hackathon-fake-blog-post-s/image1_wyiafu.png \"Conditional pipelines (within an MR)\")\n\n![Conditional pipelines (on the main branch)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738774/Blog/Imported/hackathon-fake-blog-post-s/image5_r6lkfd.png \"Conditional pipelines (on the main branch)\")\n\n### Muster 5: CI/CD Components\n\nEine Komponentendefinition aus einer gemeinsamen Bibliothek:\n\n```yaml # templates/deploy.yml spec:\n  inputs:\n    stage:\n      default: deploy\n    environment:\n      default: production\n--- deploy-job:\n  stage: $[[ inputs.stage ]]\n  script:\n    - echo \"Deploying $APP_NAME to $[[ inputs.environment ]]\"\n    - echo \"Deploy URL: $DEPLOY_URL\"\n  environment:\n    name: $[[ inputs.environment ]]\n```\n\nSo bindet ein Anwendungsteam die Komponenten ein:\n\n```yaml # Application repo: .gitlab-ci.yml variables:\n  APP_NAME: \"my-awesome-app\"\n  DEPLOY_URL: \"https://api.example.com\"\n\ninclude:\n  - component: gitlab.com/my-org/component-library/build@v1.0.6\n  - component: gitlab.com/my-org/component-library/test@v1.0.6\n  - component: gitlab.com/my-org/component-library/deploy@v1.0.6\n    inputs:\n      environment: staging\n\nstages:\n  - build\n  - test\n  - deploy\n```\n\n### Orientierung zu den Mustern 2 und 3\n\n**Muster 2 (Multi-Project-Pipelines):** Das Frontend-Repository publiziert ein API-Contract-Artifact und löst anschließend die Backend-Pipeline aus. Das Backend ruft das Artifact über die GitLab Jobs API ab und validiert es. Der `integration-test`-Job läuft dabei nur dann, wenn er von einer Upstream-Pipeline ausgelöst wurde (`$CI_PIPELINE_SOURCE == \"pipeline\"`), nicht bei einem eigenständigen Push. Die Frontend-Projekt-ID wird als CI/CD-Variable gesetzt, um Hardcoding zu vermeiden. Vollständige Konfigurationen beider Repositories: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/#2-microservices-cross-repo-multi-project-pipelines).\n\n**Muster 3 (Dynamische Child-Pipelines):** Ein `generate-config`-Job erzeugt zur Laufzeit environment-spezifische YAML-Dateien. Trigger-Jobs nutzen `extends:` für gemeinsam genutzte Konfiguration und `needs:` für sequenzielle Promotion (dev → staging → prod mit manuellem Gate). Vollständige Konfiguration: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/#3-multi-tenant--matrix-deployments-dynamic-child-pipelines).\n\n## Weiterführende Artikel\n\n- [Variable and artifact sharing in GitLab parent-child pipelines](https://about.gitlab.com/blog/variable-and-artifact-sharing-in-gitlab-parent-child-pipelines/)\n- [CI/CD inputs: Secure and preferred method to pass parameters to a pipeline](https://about.gitlab.com/blog/ci-cd-inputs-secure-and-preferred-method-to-pass-parameters-to-a-pipeline/)\n- [Tutorial: How to set up your first GitLab CI/CD component](https://about.gitlab.com/blog/tutorial-how-to-set-up-your-first-gitlab-ci-cd-component/)\n- [How to include file references in your CI/CD components](https://about.gitlab.com/blog/how-to-include-file-references-in-your-ci-cd-components/)\n- [FAQ: GitLab CI/CD Catalog](https://about.gitlab.com/blog/faq-gitlab-ci-cd-catalog/)\n- [Building a GitLab CI/CD pipeline for a monorepo the easy way](https://about.gitlab.com/blog/building-a-gitlab-ci-cd-pipeline-for-a-monorepo-the-easy-way/)\n- [A CI/CD component builder's journey](https://about.gitlab.com/blog/a-ci-component-builders-journey/)\n- [CI/CD Catalog goes GA: No more building pipelines from scratch](https://about.gitlab.com/blog/ci-cd-catalog-goes-ga-no-more-building-pipelines-from-scratch/)","5 GitLab-Pipeline-Muster für komplexe Engineering-Herausforderungen","Wie Parent-Child-Pipelines, DAG-Execution, MR-Pipelines und CI/CD Components komplexe Delivery-Probleme lösen – von Monorepos bis zur governed Plattform.",[756],"Omid Khan","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772721753/frfsm1qfscwrmsyzj1qn.png","2026-04-09",[22,760,20,761],"DevOps platform","features",{"featured":15,"template":16,"slug":763},"5-ways-gitlab-pipeline-logic-solves-real-engineering-problems",{"content":765,"config":774},{"title":766,"description":767,"authors":768,"heroImage":770,"date":771,"body":772,"category":13,"tags":773},"GitLab Container Virtual Registry mit Docker Hardened Images einrichten","Mehrere Registries hinter einem Endpunkt – GitLab Container Virtual Registry mit Docker Hardened Images, Caching und Audit-Trail.",[769],"Tim Rizzi","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772111172/mwhgbjawn62kymfwrhle.png","2026-03-12","Wer im Plattformteam arbeitet, kennt solche Gespräche:\n\n*„Security sagt: Wir müssen gehärtete Base-Images verwenden.\"*\n\n*„Prima – wo trage ich jetzt die Credentials für noch eine weitere Registry ein?\"*\n\n*„Und wie stellen wir sicher, dass alle sie auch wirklich nutzen?\"*\n\nOder diese hier:\n\n*„Warum sind unsere Builds so langsam?\"*\n\n*„Wir pullen dasselbe 500-MB-Image in jedem einzelnen Job neu von Docker Hub.\"*\n\n*„Kann man die nicht irgendwo cachen?\"*\n\nIch arbeite bei GitLab an der [Container Virtual Registry](https://docs.gitlab.com/user/packages/virtual_registry/container/) – einem Pull-Through-Cache, der vor den vorgelagerten Registries sitzt: Docker Hub, dhi.io (Docker Hardened Images), MCR und Quay. Teams erhalten einen einzigen Endpunkt zum Pullen. Images werden beim ersten Abruf gecacht; alle nachfolgenden Pulls kommen aus dem Cache. Das Entwicklungsteam muss nicht wissen, aus welchem Upstream ein bestimmtes Image stammt.\n\nDieser Artikel zeigt die Einrichtung der Container Virtual Registry – mit Docker Hardened Images als konkretem Anwendungsfall, da diese Kombination für Teams mit Sicherheitsanforderungen besonders naheliegt.\n\n## Das Problem: Registry-Wildwuchs im Plattformteam\n\nDie Plattformteams, mit denen ich spreche, verwalten Container-Images über drei bis fünf Registries:\n\n- **Docker Hub** für die meisten Base-Images\n- **dhi.io** für Docker Hardened Images (sicherheitskritische Workloads)\n- **MCR** für .NET- und Azure-Tooling\n- **Quay.io** für das Red-Hat-Ökosystem\n- **Interne Registries** für proprietäre Images\n\nJede davon hat eigene Authentifizierungsmechanismen, unterschiedliche Netzwerklatenz und eine eigene Pfadstruktur für Images.\n\nCI/CD-Konfigurationen füllen sich mit registry-spezifischer Logik. Credential-Management wird zum eigenständigen Projekt. Und jeder Pipeline-Job lädt dieselben Base-Images erneut über das Netz – obwohl sie sich seit Wochen nicht geändert haben.\n\nContainer Virtual Registry konsolidiert das: eine Registry-URL, ein Authentifizierungsfluss über GitLab, gecachte Images aus GitLab-Infrastruktur statt wiederholter Internet-Traversierung.\n\n## Funktionsweise\n\nDas Modell ist geradlinig:\n\n```text\n\nPipeline ruft ab:\n  gitlab.com/virtual_registries/container/1000016/python:3.13\n\nVirtual Registry prüft:\n  1. Im Cache vorhanden? → Direkt zurückgeben\n  2. Nein? → Vom Upstream laden, cachen, zurückgeben\n\n\n```\n\nUpstreams werden in Prioritätsreihenfolge konfiguriert. Bei einem eingehenden Pull-Request durchsucht die Virtual Registry die Upstreams der Reihe nach, bis das Image gefunden wird. Das Ergebnis wird für einen konfigurierbaren Zeitraum gecacht – standardmäßig 24 Stunden.\n\n\n```text\n\n┌─────────────────────────────────────────────────────────┐ │                    CI/CD Pipeline                       │ │                          │                              │ │                          ▼                              │ │   gitlab.com/virtual_registries/container/\u003Cid>/image   │ └─────────────────────────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────┐ │            Container Virtual Registry                   │ │                                                         │ │  Upstream 1: Docker Hub ────────────────┐               │ │  Upstream 2: dhi.io (Hardened) ────────┐│               │ │  Upstream 3: MCR ─────────────────────┐││               │ │  Upstream 4: Quay.io ────────────────┐│││               │ │                                      ││││               │ │                    ┌─────────────────┴┴┴┴──┐            │ │                    │        Cache          │            │ │                    │  (manifests + layers) │            │ │                    └───────────────────────┘            │ └─────────────────────────────────────────────────────────┘\n\n```\n\n## Was das konkret bringt – besonders mit Docker Hardened Images\n\n[Docker Hardened Images](https://docs.docker.com/dhi/) zeichnen sich durch minimale Angriffsfläche, nahezu keine bekannten CVEs, vollständige Software Bills of Materials (SBOMs) und SLSA-Provenance aus. Für Teams, die Base-Images für sicherheitskritische Workloads evaluieren, gehören sie auf die Shortlist.\n\nDer Wechsel zu dhi.io erzeugt jedoch dieselbe operative Reibung wie jede neue Registry:\n\n- **Credential-Verteilung**: Docker-Credentials müssen auf alle Systeme verteilt werden, die Images von dhi.io abrufen.\n- **CI/CD-Anpassungen**: Jede Pipeline muss für die Authentifizierung mit dhi.io aktualisiert werden.\n- **Akzeptanzproblem**: Ohne zentrale Steuerung greifen Teams weiterhin auf reguläre Images zurück.\n- **Fehlende Transparenz**: Ob Teams tatsächlich die gehärteten Varianten nutzen, ist kaum nachvollziehbar.\n\nDie Virtual Registry löst jeden dieser Punkte:\n\n**Einzelne Credential**: Teams authentifizieren sich bei GitLab. Die Virtual Registry übernimmt die Upstream-Authentifizierung. Docker-Credentials werden einmalig auf Registry-Ebene konfiguriert und gelten für alle Pulls.\n\n**Keine per-Team-CI/CD-Änderungen**: Pipelines auf die Virtual Registry zeigen lassen – fertig. Die Upstream-Konfiguration ist zentralisiert.\n\n**Schrittweise Einführung**: Da Images mit ihrem vollständigen Pfad gecacht werden, ist im Cache sichtbar, was tatsächlich abgerufen wird. Wird `library/python:3.11` statt der gehärteten Variante gepullt, ist das erkennbar.\n\n**Audit-Trail**: Der Cache zeigt exakt, welche Images aktiv genutzt werden – nachvollziehbar für Compliance-Zwecke und als Grundlage für das Verständnis der tatsächlichen Infrastruktur-Abhängigkeiten.\n\nWer das Konzept verstanden hat und die Einrichtung zu einem späteren Zeitpunkt in Angriff nimmt: Die wesentlichen Konzepte sind damit abgedeckt. Die technische Konfiguration folgt im nächsten Abschnitt.\n\n## Einrichtung\n\nDie folgende Einrichtung nutzt den Python-Client aus dem Demo-Projekt.\n\n### Virtual Registry erstellen\n\n```python\nfrom virtual_registry_client import VirtualRegistryClient\nclient = VirtualRegistryClient()\nregistry = client.create_virtual_registry(\n    group_id=\"785414\",  # ID der obersten Gruppe\n    name=\"platform-images\",\n    description=\"Cached container images for platform teams\"\n)\nprint(f\"Registry ID: {registry['id']}\") # Diese ID wird für die Pull-URL benötigt\n```\n\n### Docker Hub als Upstream hinzufügen\n\nFür offizielle Images wie Alpine, Python usw.:\n\n```python\n\ndocker_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://registry-1.docker.io\",\n    name=\"Docker Hub\",\n    cache_validity_hours=24\n)\n\n```\n\n### Docker Hardened Images (dhi.io) hinzufügen\n\nDocker Hardened Images werden auf `dhi.io` gehostet – einer separaten Registry mit Authentifizierungspflicht:\n\n```python\n\ndhi_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-docker-username\",\n    password=\"your-docker-access-token\",\n    cache_validity_hours=24\n)\n\n```\n\n### Weitere Upstreams hinzufügen\n\n```python\n\n# MCR für .NET-Teams client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://mcr.microsoft.com\",\n    name=\"Microsoft Container Registry\",\n    cache_validity_hours=48\n)\n# Quay für das Red-Hat-Ökosystem client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://quay.io\",\n    name=\"Quay.io\",\n    cache_validity_hours=24\n)\n\n```\n\n### CI/CD aktualisieren\n\nEine `.gitlab-ci.yml`, die über die Virtual Registry pullt:\n\n```yaml\n\nvariables:\n  VIRTUAL_REGISTRY_ID: \u003Cyour_virtual_registry_ID>\n\n  \nbuild:\n  image: docker:24\n  services:\n    - docker:24-dind\n  before_script:\n    # Authentifizierung bei GitLab – Upstream-Auth wird übernommen\n    - echo \"${CI_JOB_TOKEN}\" | docker login -u gitlab-ci-token --password-stdin gitlab.com\n  script:\n    # Alle Pulls laufen über die zentrale Virtual Registry\n    \n    # Offizielle Docker Hub Images (library/-Präfix erforderlich)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest\n    \n    # Docker Hardened Images von dhi.io (kein Präfix nötig)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13\n    \n    # .NET von MCR\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0\n\n\n```\n\n### Image-Pfadformate\n\nVerschiedene Registries verwenden unterschiedliche Pfadkonventionen:\n\n| Registry | Beispiel-Pull-URL |\n|----------|-------------------|\n| Docker Hub (offiziell) | `.../library/python:3.11-slim` |\n| Docker Hardened Images (dhi.io) | `.../python:3.13` |\n| MCR | `.../dotnet/sdk:8.0` |\n| Quay.io | `.../prometheus/prometheus:latest` |\n\n### Funktionsprüfung\n\nNach einigen Pulls lässt sich der Cache überprüfen:\n\n```python\n\nupstreams = client.list_registry_upstreams(registry['id']) for upstream in upstreams:\n    entries = client.list_cache_entries(upstream['id'])\n    print(f\"{upstream['name']}: {len(entries)} cached entries\")\n\n\n```\n\n## Messergebnisse\n\nTestergebnisse beim Pullen über die Virtual Registry:\n\n| Messgröße | Ohne Cache | Mit warmem Cache |\n|-----------|------------|-----------------|\n| Pull-Zeit (Alpine) | 10,3 s | 4,2 s |\n| Pull-Zeit (Python 3.13 DHI) | 11,6 s | ~4 s |\n| Netzwerk-Roundtrips zum Upstream | Jeder Pull | Nur Cache-Misses |\n\nDer erste Pull hat dieselbe Dauer – das Image muss vom Upstream geladen werden. Jeder weitere Pull innerhalb der Cache-Gültigkeitsdauer kommt direkt aus GitLab-Storage: kein Netzwerk-Hop zu Docker Hub, dhi.io, MCR oder einer anderen Registry.\n\nBei Teams mit vielen Pipeline-Jobs pro Tag summiert sich das zu einem messbaren Gewinn bei den Build-Laufzeiten.\n\n## Praktische Hinweise\n\n### Cache-Gültigkeit\n\nDer Standard sind 24 Stunden. Für sicherheitskritische Images, bei denen Patches schnell verfügbar sein sollen, empfiehlt sich ein kürzeres Intervall:\n\n```python\n\nclient.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-username\",\n    password=\"your-token\",\n    cache_validity_hours=12\n)\n\n```\n\nFür stabile Images mit fixen Versions-Tags ist ein längeres Intervall problemlos.\n\n### Upstream-Priorität\n\nUpstreams werden der Reihe nach geprüft. Bei gleichnamigen Images in verschiedenen Registries gewinnt der erste passende Upstream.\n\n### Limits\n\n- Maximal 20 Virtual Registries pro Gruppe\n- Maximal 20 Upstreams pro Virtual Registry\n\n## Konfiguration über die Oberfläche\n\nVirtual Registries und Upstreams lassen sich auch direkt in der GitLab-Oberfläche einrichten – ohne API-Aufrufe. Unter **Einstellungen > Pakete und Registries > Virtual Registry** der jeweiligen Gruppe stehen folgende Optionen zur Verfügung:\n\n- Virtual Registries erstellen und verwalten\n- Upstreams hinzufügen, bearbeiten und neu anordnen\n- Cache anzeigen und verwalten\n- Überblick, welche Images abgerufen werden\n\n## Ausblick\n\nIn Entwicklung:\n\n- **Allow/Deny-Listen**: Regex-basierte Steuerung, welche Images aus welchen Upstreams abgerufen werden dürfen.\n\nContainer Virtual Registry befindet sich in der Beta-Phase. Die Funktion wird produktiv eingesetzt und wird weiterentwickelt – Feedback fließt direkt in die Priorisierung ein.\n\n## Feedback\n\nWer als Plattformteam mit Registry-Wildwuchs zu kämpfen hat: Ich möchte verstehen, wie die aktuelle Situation aussieht.\n\n- Wie viele Upstream-Registries werden verwaltet?\n- Wo liegt der größte Schmerzpunkt?\n- Würde ein solcher Ansatz helfen – und falls nicht: Was fehlt?\n\nErfahrungen und Rückmeldungen gerne im [Container Virtual Registry Feedback-Issue](https://gitlab.com/gitlab-org/gitlab/-/work_items/589630) teilen.\n\n## Weiterführende Ressourcen\n\n- [Neue GitLab-Metriken und Registry-Funktionen zur Optimierung von CI/CD-Pipelines](https://about.gitlab.com/de-de/blog/new-gitlab-metrics-and-registry-features-help-reduce-ci-cd-bottlenecks/)\n- [Container Virtual Registry – Dokumentation](https://docs.gitlab.com/user/packages/virtual_registry/container/)\n- [Container Virtual Registry – API](https://docs.gitlab.com/api/container_virtual_registries/)\n\n## Für deutsche Unternehmen könnte dies folgende Themen betreffen\n\nTeams, die sicherheitsgehärtete Base-Images mit vollständigen SBOMs und SLSA-Provenance einsetzen, haben möglicherweise auch Compliance-Überlegungen – beispielsweise in Bereichen wie Sicherheit der Software-Lieferkette, Nachvollziehbarkeit von Image-Abhängigkeiten und zentralem Audit-Trail.\n\nRegulatorische Frameworks wie NIS2 und der Cyber Resilience Act adressieren ähnliche Themen rund um Software-Lieferketten und SBOM-Transparenz. Für konkrete Compliance-Anforderungen empfiehlt sich Rücksprache mit entsprechender Fachberatung.",[20,747,761],{"featured":474,"template":16,"slug":775},"using-gitlab-container-virtual-registry-with-docker-hardened-images",{"promotions":777},[778,792,803,815],{"id":779,"categories":780,"header":782,"text":783,"button":784,"image":789},"ai-modernization",[781],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":785,"config":786},"Get your AI maturity score",{"href":787,"dataGaName":788,"dataGaLocation":253},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":790},{"src":791},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":793,"categories":794,"header":795,"text":783,"button":796,"image":800},"devops-modernization",[747,581],"Are you just managing tools or shipping innovation?",{"text":797,"config":798},"Get your DevOps maturity score",{"href":799,"dataGaName":788,"dataGaLocation":253},"/assessments/devops-modernization-assessment/",{"config":801},{"src":802},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":804,"categories":805,"header":807,"text":783,"button":808,"image":812},"security-modernization",[806],"security","Are you trading speed for security?",{"text":809,"config":810},"Get your security maturity score",{"href":811,"dataGaName":788,"dataGaLocation":253},"/assessments/security-modernization-assessment/",{"config":813},{"src":814},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":816,"paths":817,"header":819,"text":820,"button":821,"image":826},"github-azure-migration",[17,818],"integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":822,"config":823},"See how GitLab compares to GitHub",{"href":824,"dataGaName":825,"dataGaLocation":253},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":827},{"src":802},{"header":829,"blurb":830,"button":831,"secondaryButton":836},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":832,"config":833},"Kostenlosen Test starten",{"href":834,"dataGaName":51,"dataGaLocation":835},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/de-de/","feature",{"text":53,"config":837},{"href":55,"dataGaName":56,"dataGaLocation":835},1777934794755]