[{"data":1,"prerenderedAt":779},["ShallowReactive",2],{"/de-de/blog/automating-container-image-migration-from-amazon-ecr-to-gitlab":3,"navigation-de-de":45,"banner-de-de":447,"footer-de-de":457,"blog-post-authors-de-de-Tim Rizzi":662,"blog-related-posts-de-de-automating-container-image-migration-from-amazon-ecr-to-gitlab":676,"blog-promotions-de-de":716,"next-steps-de-de":769},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":30,"isFeatured":12,"meta":31,"navigation":12,"path":32,"publishedDate":20,"seo":33,"stem":38,"tagSlugs":39,"__hash__":44},"blogPosts/de-de/blog/automating-container-image-migration-from-amazon-ecr-to-gitlab.yml","Automating Container Image Migration From Amazon Ecr To Gitlab",[7],"tim-rizzi",null,"engineering",{"slug":11,"featured":12,"template":13},"automating-container-image-migration-from-amazon-ecr-to-gitlab",true,"BlogPost",{"title":15,"description":16,"authors":17,"heroImage":19,"date":20,"body":21,"category":9,"tags":22,"updatedDate":29},"Automatisierung der Migration von Container-Images von Amazon ECR zu GitLab","Wenn Plattformteams ihre CI/CD zu GitLab verschieben, sollte die Migration von Container-Images kein Engpass sein. Befolge diese Schritt-für-Schritt-Anleitung, um den Pipeline-Migrationsprozess zu automatisieren.",[18],"Tim Rizzi","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663129/Blog/Hero%20Images/blog-image-template-1800x945__28_.png","2025-02-13","„Wir müssen Hunderte von Container-Images von Amazon Elastic Container Registry (ECR) zu GitLab migrieren. Könnt ihr uns helfen?“ Diese Frage taucht immer wieder in Gesprächen mit Platform Engineers auf. Sie modernisierten also ihre DevSecOps-Toolchain mit GitLab, kamen aber beim Verschieben ihrer Container-Images nicht weiter. Während ein einzelner Image-Transfer einfach ist, erscheint das schiere Volumen überwältigend.\n\nEin Platform Engineer brachte es auf den Punkt: „Ich weiß genau, was zu tun ist – pullen, neu kennzeichnen, pushen. Aber ich habe 200 Microservices mit jeweils mehreren Tags. Ich kann es nicht rechtfertigen, Wochen für diese Migration aufzuwenden, während wichtige Infrastrukturarbeiten anstehen.“\n\n## Die Herausforderung\n\nDieses Gespräch führte zu einer Idee. Was wäre, wenn wir den gesamten Prozess automatisieren könnten? Wenn Plattformteams ihre [CI/CD](https://about.gitlab.com/de-de/topics/ci-cd/) zu GitLab verschieben, sollte die Migration von Container-Images kein Engpass sein. Der manuelle Prozess ist einfach, aber repetitiv: Jedes Image pullen, neu kennzeichnen und in die Container-Registry von GitLab pushen. Angesichts dutzender Repositories und mehrerer Tags pro Image, bedeutet dies Tage oder Wochen langwieriger Arbeit.\n\n## Die Lösung\n\nWir haben uns daran gemacht, eine GitLab-Pipeline zu erstellen, die all diese schwierigen Aufgaben automatisch erledigt. Das Ziel war einfach: Wir wollten Platform Engineers ein Tool zur Verfügung stellen, das sie in wenigen Minuten einrichten und über Nacht ausführen können, sodass am nächsten Morgen alle ihre Images erfolgreich migriert wurden.\n\n### Einrichten des Zugriffs\n\nDas Wichtigste zuerst: die Sicherheit. Teams sollen diese Migration mit minimalen AWS-Berechtigungen durchführen können. Hier ist die schreibgeschützte Identitäts- und Zugriffsmanagement (IAM)-Richtlinie dafür:\n\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ecr:GetAuthorizationToken\",\n                \"ecr:BatchCheckLayerAvailability\",\n                \"ecr:GetDownloadUrlForLayer\",\n                \"ecr:DescribeRepositories\",\n                \"ecr:ListImages\",\n                \"ecr:DescribeImages\",\n                \"ecr:BatchGetImage\"\n            ],\n            \"Resource\": \"*\"\n        }\n    ]\n}\n```\n\n### GitLab-Konfiguration\n\nNachdem die Sicherheit gewährleistet ist, ist der nächste Schritt die Einrichtung von GitLab. Wir haben uns auf das Wesentliche beschränkt. Du musst diese Variablen in deinen CI/CD-Einstellungen konfigurieren:\n\n```yaml\nAWS_ACCOUNT_ID: Your AWS account number\nAWS_DEFAULT_REGION: Your ECR region\nAWS_ACCESS_KEY_ID: [Masked]\nAWS_SECRET_ACCESS_KEY: [Masked]\nBULK_MIGRATE: true\n```\n\n### Die Migrationspipeline\n\nJetzt wird es interessant. Wir haben die Pipeline mit Docker-in-Docker erstellt, um alle Image-Vorgänge zuverlässig zu verarbeiten:\n\n```yaml\nimage: docker:20.10\nservices:\n  - docker:20.10-dind\n\nbefore_script:\n  - apk add --no-cache aws-cli jq\n  - aws sts get-caller-identity\n  - aws ecr get-login-password | docker login --username AWS --password-stdin\n  - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}\n```\n\nDie Pipeline hat drei Phasen, die jeweils auf der letzten aufbauen:\n\n1. Entdeckung\n\nZuerst findet sie alle deine Repositories:\n\n```bash\nREPOS=$(aws ecr describe-repositories --query 'repositories[*].repositoryName' --output text)\n```\n\n2. Tag-Auflistung\n\nDann ruft sie für jedes Repository alle Tags ab:\n\n```bash\nTAGS=$(aws ecr describe-images --repository-name $repo --query 'imageDetails[*].imageTags[]' --output text)\n```\n\n3. Übertragung\n\nSchließlich übernimmt sie die eigentliche Migration:\n\n```bash\ndocker pull ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${repo}:${tag}\ndocker tag ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${repo}:${tag} ${CI_REGISTRY_IMAGE}/${repo}:${tag}\ndocker push ${CI_REGISTRY_IMAGE}/${repo}:${tag}\n```\n\n## Das Ergebnis\n\nErinnerst du dich an den Platform Engineer, der nicht wochenlang mit der Migration verbringen wollte? Diese Lösung bietet Folgendes:\n\n- automatisierte Erkennung und Migration aller Repositories und Tags\n- konsistente Image-Benennung zwischen ECR und GitLab\n- Fehlerbehandlung für fehlgeschlagene Übertragungen\n- klare Protokollierung zur Verfolgung des Fortschritts\n\nAnstatt Skripte zu schreiben und die Migration zu überwachen, konnte sich der Plattform Engineer auf wertvollere Arbeit konzentrieren.\n\n## Verwendung\n\nDie ersten Schritte sind einfach:\n\n1. Kopiere die Datei `.gitlab-ci.yml` in dein Repository.\n2. Konfiguriere die AWS- und GitLab-Variablen.\n3. Setze `BULK_MIGRATE` auf „true“, um die Migration zu starten.\n\n## Best Practices\n\nBei der Unterstützung von Teams bei ihren Migrationen haben wir einige Dinge gelernt:\n\n- Der Prozess sollte außerhalb der Stoßzeiten durchgeführt werden, um die Auswirkungen auf dein Team zu minimieren.\n- Die Pipeline-Protokolle sind wichtig – darin findest du Informationen, wenn es ein Problem gibt.\n- Die Elastic Container Registry (ECR) sollte erst deaktiviert werden, wenn du überprüft hast, dass alle Images erfolgreich übertragen wurden.\n- Bei sehr großen Migrationen solltest du eine Ratenbegrenzung in Betracht ziehen, um eine Überlastung deines Netzwerks zu vermeiden.\n\nWir haben diese Pipeline in unserem öffentlichen GitLab-Repository quelloffen verfügbar gemacht, weil wir der Meinung sind, dass Plattform Engineers Zeit damit verbringen sollten, wertvolle Infrastrukturen aufzubauen, anstatt Container-Images zu kopieren. Passe sie an deine Bedürfnisse an und stelle Fragen zur Implementierung, wenn etwas unklar ist.\n\n> #### Informationen dazu und zu anderen Paketkomponenten findest du in unserer [CI/CD-Katalog-Dokumentation](https://gitlab.com/explore/catalog/components/package).",[23,24,25,26,27,28],"CI/CD","AWS","tutorial","DevSecOps platform","product","solutions architecture","2025-04-10","yml",{},"/de-de/blog/automating-container-image-migration-from-amazon-ecr-to-gitlab",{"title":15,"description":16,"ogTitle":15,"ogDescription":16,"noIndex":34,"ogImage":19,"ogUrl":35,"ogSiteName":36,"ogType":37,"canonicalUrls":35},false,"https://about.gitlab.com/blog/automating-container-image-migration-from-amazon-ecr-to-gitlab","https://about.gitlab.com","article","de-de/blog/automating-container-image-migration-from-amazon-ecr-to-gitlab",[40,41,25,42,27,43],"cicd","aws","devsecops-platform","solutions-architecture","7QBEee5r9vQkVr5Ta0pKfVQTxObYENMtJLE7z1pq9d0",{"data":46},{"logo":47,"freeTrial":52,"sales":57,"login":62,"items":67,"search":375,"minimal":410,"duo":428,"pricingDeployment":437},{"config":48},{"href":49,"dataGaName":50,"dataGaLocation":51},"/de-de/","gitlab logo","header",{"text":53,"config":54},"Kostenlose Testversion anfordern",{"href":55,"dataGaName":56,"dataGaLocation":51},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de&glm_content=default-saas-trial/","free trial",{"text":58,"config":59},"Vertrieb kontaktieren",{"href":60,"dataGaName":61,"dataGaLocation":51},"/de-de/sales/","sales",{"text":63,"config":64},"Anmelden",{"href":65,"dataGaName":66,"dataGaLocation":51},"https://gitlab.com/users/sign_in/","sign in",[68,95,190,195,296,356],{"text":69,"config":70,"cards":72},"Plattform",{"dataNavLevelOne":71},"platform",[73,79,87],{"title":69,"description":74,"link":75},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":76,"config":77},"Erkunde unsere Plattform",{"href":78,"dataGaName":71,"dataGaLocation":51},"/de-de/platform/",{"title":80,"description":81,"link":82},"GitLab Duo Agent Platform","Agentische KI für den gesamten Softwareentwicklungszyklus",{"text":83,"config":84},"Lerne GitLab Duo kennen",{"href":85,"dataGaName":86,"dataGaLocation":51},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":88,"description":89,"link":90},"Gründe, die für GitLab sprechen","Erfahre, warum Unternehmen auf GitLab setzen",{"text":91,"config":92},"Mehr erfahren",{"href":93,"dataGaName":94,"dataGaLocation":51},"/de-de/why-gitlab/","why gitlab",{"text":96,"left":12,"config":97,"link":99,"lists":103,"footer":172},"Produkt",{"dataNavLevelOne":98},"solutions",{"text":100,"config":101},"Alle Lösungen anzeigen",{"href":102,"dataGaName":98,"dataGaLocation":51},"/de-de/solutions/",[104,128,150],{"title":105,"description":106,"link":107,"items":112},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":108},{"icon":109,"href":110,"dataGaName":111,"dataGaLocation":51},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[113,116,119,124],{"text":23,"config":114},{"href":115,"dataGaLocation":51,"dataGaName":23},"/de-de/solutions/continuous-integration/",{"text":80,"config":117},{"href":85,"dataGaLocation":51,"dataGaName":118},"gitlab duo agent platform - product menu",{"text":120,"config":121},"Quellcodeverwaltung",{"href":122,"dataGaLocation":51,"dataGaName":123},"/de-de/solutions/source-code-management/","Source Code Management",{"text":125,"config":126},"Automatisierte Softwarebereitstellung",{"href":110,"dataGaLocation":51,"dataGaName":127},"Automated software delivery",{"title":129,"description":130,"link":131,"items":136},"Sicherheit","Entwickle schneller, ohne die Sicherheit zu gefährden",{"config":132},{"href":133,"dataGaName":134,"dataGaLocation":51,"icon":135},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[137,141,146],{"text":138,"config":139},"Application Security Testing",{"href":133,"dataGaName":140,"dataGaLocation":51},"Application security testing",{"text":142,"config":143},"Schutz der Software-Lieferkette",{"href":144,"dataGaLocation":51,"dataGaName":145},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":147,"config":148},"Software Compliance",{"href":149,"dataGaName":147,"dataGaLocation":51},"/de-de/solutions/software-compliance/",{"title":151,"link":152,"items":157},"Bewertung",{"config":153},{"icon":154,"href":155,"dataGaName":156,"dataGaLocation":51},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[158,162,167],{"text":159,"config":160},"Sichtbarkeit und Bewertung",{"href":155,"dataGaLocation":51,"dataGaName":161},"Visibility and Measurement",{"text":163,"config":164},"Wertstrommanagement",{"href":165,"dataGaLocation":51,"dataGaName":166},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":168,"config":169},"Analysen und Einblicke",{"href":170,"dataGaLocation":51,"dataGaName":171},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":173,"items":174},"GitLab für",[175,180,185],{"text":176,"config":177},"Enterprise",{"href":178,"dataGaLocation":51,"dataGaName":179},"/de-de/enterprise/","enterprise",{"text":181,"config":182},"Kleinunternehmen",{"href":183,"dataGaLocation":51,"dataGaName":184},"/de-de/small-business/","small business",{"text":186,"config":187},"den öffentlichen Sektor",{"href":188,"dataGaLocation":51,"dataGaName":189},"/de-de/solutions/public-sector/","public sector",{"text":191,"config":192},"Preise",{"href":193,"dataGaName":194,"dataGaLocation":51,"dataNavLevelOne":194},"/de-de/pricing/","pricing",{"text":196,"config":197,"link":199,"lists":203,"feature":283},"Ressourcen",{"dataNavLevelOne":198},"resources",{"text":200,"config":201},"Alle Ressourcen anzeigen",{"href":202,"dataGaName":198,"dataGaLocation":51},"/de-de/resources/",[204,237,255],{"title":205,"items":206},"Erste Schritte",[207,212,217,222,227,232],{"text":208,"config":209},"Installieren",{"href":210,"dataGaName":211,"dataGaLocation":51},"/de-de/install/","install",{"text":213,"config":214},"Kurzanleitungen",{"href":215,"dataGaName":216,"dataGaLocation":51},"/de-de/get-started/","quick setup checklists",{"text":218,"config":219},"Lernen",{"href":220,"dataGaLocation":51,"dataGaName":221},"https://university.gitlab.com/","learn",{"text":223,"config":224},"Produktdokumentation",{"href":225,"dataGaName":226,"dataGaLocation":51},"https://docs.gitlab.com/","product documentation",{"text":228,"config":229},"Best-Practice-Videos",{"href":230,"dataGaName":231,"dataGaLocation":51},"/de-de/getting-started-videos/","best practice videos",{"text":233,"config":234},"Integrationen",{"href":235,"dataGaName":236,"dataGaLocation":51},"/de-de/integrations/","integrations",{"title":238,"items":239},"Entdecken",[240,245,250],{"text":241,"config":242},"Kundenerfolge",{"href":243,"dataGaName":244,"dataGaLocation":51},"/de-de/customers/","customer success stories",{"text":246,"config":247},"Blog",{"href":248,"dataGaName":249,"dataGaLocation":51},"/de-de/blog/","blog",{"text":251,"config":252},"Remote",{"href":253,"dataGaName":254,"dataGaLocation":51},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":256,"items":257},"Vernetzen",[258,263,268,273,278],{"text":259,"config":260},"GitLab-Services",{"href":261,"dataGaName":262,"dataGaLocation":51},"/de-de/services/","services",{"text":264,"config":265},"Community",{"href":266,"dataGaName":267,"dataGaLocation":51},"/community/","community",{"text":269,"config":270},"Forum",{"href":271,"dataGaName":272,"dataGaLocation":51},"https://forum.gitlab.com/","forum",{"text":274,"config":275},"Veranstaltungen",{"href":276,"dataGaName":277,"dataGaLocation":51},"/events/","events",{"text":279,"config":280},"Partner",{"href":281,"dataGaName":282,"dataGaLocation":51},"/de-de/partners/","partners",{"backgroundColor":284,"textColor":285,"text":286,"image":287,"link":291},"#2f2a6b","#fff","Perspektiven für die Softwareentwicklung der Zukunft",{"altText":288,"config":289},"the source promo card",{"src":290},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":292,"config":293},"Lies die News",{"href":294,"dataGaName":295,"dataGaLocation":51},"/de-de/the-source/","the source",{"text":297,"config":298,"lists":300},"Unternehmen",{"dataNavLevelOne":299},"company",[301],{"items":302},[303,308,314,316,321,326,331,336,341,346,351],{"text":304,"config":305},"Über",{"href":306,"dataGaName":307,"dataGaLocation":51},"/de-de/company/","about",{"text":309,"config":310,"footerGa":313},"Karriere",{"href":311,"dataGaName":312,"dataGaLocation":51},"/jobs/","jobs",{"dataGaName":312},{"text":274,"config":315},{"href":276,"dataGaName":277,"dataGaLocation":51},{"text":317,"config":318},"Geschäftsführung",{"href":319,"dataGaName":320,"dataGaLocation":51},"/company/team/e-group/","leadership",{"text":322,"config":323},"Team",{"href":324,"dataGaName":325,"dataGaLocation":51},"/company/team/","team",{"text":327,"config":328},"Handbuch",{"href":329,"dataGaName":330,"dataGaLocation":51},"https://handbook.gitlab.com/","handbook",{"text":332,"config":333},"Investor Relations",{"href":334,"dataGaName":335,"dataGaLocation":51},"https://ir.gitlab.com/","investor relations",{"text":337,"config":338},"Trust Center",{"href":339,"dataGaName":340,"dataGaLocation":51},"/de-de/security/","trust center",{"text":342,"config":343},"AI Transparency Center",{"href":344,"dataGaName":345,"dataGaLocation":51},"/de-de/ai-transparency-center/","ai transparency center",{"text":347,"config":348},"Newsletter",{"href":349,"dataGaName":350,"dataGaLocation":51},"/company/contact/#contact-forms","newsletter",{"text":352,"config":353},"Presse",{"href":354,"dataGaName":355,"dataGaLocation":51},"/press/","press",{"text":357,"config":358,"lists":359},"Kontakt",{"dataNavLevelOne":299},[360],{"items":361},[362,365,370],{"text":58,"config":363},{"href":60,"dataGaName":364,"dataGaLocation":51},"talk to sales",{"text":366,"config":367},"Support-Portal",{"href":368,"dataGaName":369,"dataGaLocation":51},"https://support.gitlab.com","support portal",{"text":371,"config":372},"Kundenportal",{"href":373,"dataGaName":374,"dataGaLocation":51},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":376,"login":377,"suggestions":384},"Schließen",{"text":378,"link":379},"Um Repositories und Projekte zu durchsuchen, melde dich an bei",{"text":380,"config":381},"gitlab.com",{"href":65,"dataGaName":382,"dataGaLocation":383},"search login","search",{"text":385,"default":386},"Vorschläge",[387,389,394,396,401,406],{"text":80,"config":388},{"href":85,"dataGaName":80,"dataGaLocation":383},{"text":390,"config":391},"Code Suggestions (KI)",{"href":392,"dataGaName":393,"dataGaLocation":383},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":23,"config":395},{"href":115,"dataGaName":23,"dataGaLocation":383},{"text":397,"config":398},"GitLab auf AWS",{"href":399,"dataGaName":400,"dataGaLocation":383},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":402,"config":403},"GitLab auf Google Cloud",{"href":404,"dataGaName":405,"dataGaLocation":383},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":407,"config":408},"Warum GitLab?",{"href":93,"dataGaName":409,"dataGaLocation":383},"Why GitLab?",{"freeTrial":411,"mobileIcon":416,"desktopIcon":421,"secondaryButton":424},{"text":412,"config":413},"Kostenlos testen",{"href":414,"dataGaName":56,"dataGaLocation":415},"https://gitlab.com/-/trials/new/","nav",{"altText":417,"config":418},"GitLab-Symbol",{"src":419,"dataGaName":420,"dataGaLocation":415},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":417,"config":422},{"src":423,"dataGaName":420,"dataGaLocation":415},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":205,"config":425},{"href":426,"dataGaName":427,"dataGaLocation":415},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/get-started/","get started",{"freeTrial":429,"mobileIcon":433,"desktopIcon":435},{"text":430,"config":431},"Erfahre mehr über GitLab Duo",{"href":85,"dataGaName":432,"dataGaLocation":415},"gitlab duo",{"altText":417,"config":434},{"src":419,"dataGaName":420,"dataGaLocation":415},{"altText":417,"config":436},{"src":423,"dataGaName":420,"dataGaLocation":415},{"freeTrial":438,"mobileIcon":443,"desktopIcon":445},{"text":439,"config":440},"Zurück zur Preisübersicht",{"href":193,"dataGaName":441,"dataGaLocation":415,"icon":442},"back to pricing","GoBack",{"altText":417,"config":444},{"src":419,"dataGaName":420,"dataGaLocation":415},{"altText":417,"config":446},{"src":423,"dataGaName":420,"dataGaLocation":415},{"title":448,"button":449,"config":454},"Sieh dir an, wie agentische KI die Softwarebereitstellung transformiert",{"text":450,"config":451},"GitLab Transcend jetzt ansehen",{"href":452,"dataGaName":453,"dataGaLocation":51},"/de-de/events/transcend/virtual/","transcend event",{"layout":455,"icon":456,"disabled":12},"release","AiStar",{"data":458},{"text":459,"source":460,"edit":466,"contribute":471,"config":476,"items":481,"minimal":654},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":461,"config":462},"Quelltext der Seite anzeigen",{"href":463,"dataGaName":464,"dataGaLocation":465},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":467,"config":468},"Diese Seite bearbeiten",{"href":469,"dataGaName":470,"dataGaLocation":465},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":472,"config":473},"Beteilige dich",{"href":474,"dataGaName":475,"dataGaLocation":465},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":477,"facebook":478,"youtube":479,"linkedin":480},"https://x.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[482,505,560,587,621],{"title":69,"links":483,"subMenu":488},[484],{"text":485,"config":486},"DevSecOps-Plattform",{"href":78,"dataGaName":487,"dataGaLocation":465},"devsecops platform",[489],{"title":191,"links":490},[491,495,500],{"text":492,"config":493},"Tarife anzeigen",{"href":193,"dataGaName":494,"dataGaLocation":465},"view plans",{"text":496,"config":497},"Vorteile von Premium",{"href":498,"dataGaName":499,"dataGaLocation":465},"/de-de/pricing/premium/","why premium",{"text":501,"config":502},"Vorteile von Ultimate",{"href":503,"dataGaName":504,"dataGaLocation":465},"/de-de/pricing/ultimate/","why ultimate",{"title":506,"links":507},"Lösungen",[508,513,516,518,523,528,532,535,538,543,545,547,550,555],{"text":509,"config":510},"Digitale Transformation",{"href":511,"dataGaName":512,"dataGaLocation":465},"/de-de/topics/digital-transformation/","digital transformation",{"text":514,"config":515},"Sicherheit und Compliance",{"href":133,"dataGaName":140,"dataGaLocation":465},{"text":125,"config":517},{"href":110,"dataGaName":111,"dataGaLocation":465},{"text":519,"config":520},"Agile Entwicklung",{"href":521,"dataGaName":522,"dataGaLocation":465},"/de-de/solutions/agile-delivery/","agile delivery",{"text":524,"config":525},"Cloud-Transformation",{"href":526,"dataGaName":527,"dataGaLocation":465},"/de-de/topics/cloud-native/","cloud transformation",{"text":529,"config":530},"SCM",{"href":122,"dataGaName":531,"dataGaLocation":465},"source code management",{"text":23,"config":533},{"href":115,"dataGaName":534,"dataGaLocation":465},"continuous integration & delivery",{"text":163,"config":536},{"href":165,"dataGaName":537,"dataGaLocation":465},"value stream management",{"text":539,"config":540},"GitOps",{"href":541,"dataGaName":542,"dataGaLocation":465},"/de-de/solutions/gitops/","gitops",{"text":176,"config":544},{"href":178,"dataGaName":179,"dataGaLocation":465},{"text":181,"config":546},{"href":183,"dataGaName":184,"dataGaLocation":465},{"text":548,"config":549},"Öffentlicher Sektor",{"href":188,"dataGaName":189,"dataGaLocation":465},{"text":551,"config":552},"Bildungswesen",{"href":553,"dataGaName":554,"dataGaLocation":465},"/de-de/solutions/education/","education",{"text":556,"config":557},"Finanzdienstleistungen",{"href":558,"dataGaName":559,"dataGaLocation":465},"/de-de/solutions/finance/","financial services",{"title":196,"links":561},[562,564,566,568,571,573,575,577,579,581,583,585],{"text":208,"config":563},{"href":210,"dataGaName":211,"dataGaLocation":465},{"text":213,"config":565},{"href":215,"dataGaName":216,"dataGaLocation":465},{"text":218,"config":567},{"href":220,"dataGaName":221,"dataGaLocation":465},{"text":223,"config":569},{"href":225,"dataGaName":570,"dataGaLocation":465},"docs",{"text":246,"config":572},{"href":248,"dataGaName":249,"dataGaLocation":465},{"text":241,"config":574},{"href":243,"dataGaName":244,"dataGaLocation":465},{"text":251,"config":576},{"href":253,"dataGaName":254,"dataGaLocation":465},{"text":259,"config":578},{"href":261,"dataGaName":262,"dataGaLocation":465},{"text":264,"config":580},{"href":266,"dataGaName":267,"dataGaLocation":465},{"text":269,"config":582},{"href":271,"dataGaName":272,"dataGaLocation":465},{"text":274,"config":584},{"href":276,"dataGaName":277,"dataGaLocation":465},{"text":279,"config":586},{"href":281,"dataGaName":282,"dataGaLocation":465},{"title":297,"links":588},[589,591,593,595,597,599,601,605,610,612,614,616],{"text":304,"config":590},{"href":306,"dataGaName":299,"dataGaLocation":465},{"text":309,"config":592},{"href":311,"dataGaName":312,"dataGaLocation":465},{"text":317,"config":594},{"href":319,"dataGaName":320,"dataGaLocation":465},{"text":322,"config":596},{"href":324,"dataGaName":325,"dataGaLocation":465},{"text":327,"config":598},{"href":329,"dataGaName":330,"dataGaLocation":465},{"text":332,"config":600},{"href":334,"dataGaName":335,"dataGaLocation":465},{"text":602,"config":603},"Sustainability",{"href":604,"dataGaName":602,"dataGaLocation":465},"/sustainability/",{"text":606,"config":607},"Vielfalt, Inklusion und Zugehörigkeit",{"href":608,"dataGaName":609,"dataGaLocation":465},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":337,"config":611},{"href":339,"dataGaName":340,"dataGaLocation":465},{"text":347,"config":613},{"href":349,"dataGaName":350,"dataGaLocation":465},{"text":352,"config":615},{"href":354,"dataGaName":355,"dataGaLocation":465},{"text":617,"config":618},"Transparenzerklärung zu moderner Sklaverei",{"href":619,"dataGaName":620,"dataGaLocation":465},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":622,"links":623},"Nimm Kontakt auf",[624,627,632,634,639,644,649],{"text":625,"config":626},"Sprich mit einem Experten/einer Expertin",{"href":60,"dataGaName":61,"dataGaLocation":465},{"text":628,"config":629},"Support",{"href":630,"dataGaName":631,"dataGaLocation":465},"https://support.gitlab.com/hc/en-us/articles/11626483177756-GitLab-Support","get help",{"text":371,"config":633},{"href":373,"dataGaName":374,"dataGaLocation":465},{"text":635,"config":636},"Status",{"href":637,"dataGaName":638,"dataGaLocation":465},"https://status.gitlab.com/","status",{"text":640,"config":641},"Nutzungsbedingungen",{"href":642,"dataGaName":643,"dataGaLocation":465},"/terms/","terms of use",{"text":645,"config":646},"Datenschutzerklärung",{"href":647,"dataGaName":648,"dataGaLocation":465},"/de-de/privacy/","privacy statement",{"text":650,"config":651},"Cookie-Einstellungen",{"dataGaName":652,"dataGaLocation":465,"id":653,"isOneTrustButton":12},"cookie preferences","ot-sdk-btn",{"items":655},[656,658,660],{"text":640,"config":657},{"href":642,"dataGaName":643,"dataGaLocation":465},{"text":645,"config":659},{"href":647,"dataGaName":648,"dataGaLocation":465},{"text":650,"config":661},{"dataGaName":652,"dataGaLocation":465,"id":653,"isOneTrustButton":12},[663],{"id":664,"title":18,"body":8,"config":665,"content":667,"description":8,"extension":30,"meta":671,"navigation":12,"path":672,"seo":673,"stem":674,"__hash__":675},"blogAuthors/en-us/blog/authors/tim-rizzi.yml",{"template":666},"BlogAuthor",{"name":18,"config":668},{"headshot":669,"ctfId":670},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749661866/Blog/Author%20Headshots/trizzi-headshot.jpg","trizzi",{},"/en-us/blog/authors/tim-rizzi",{},"en-us/blog/authors/tim-rizzi","ADPqrpcnKveFJS0m_zFV0VLtb_h_txu59QVgz_YwKMc",[677,691,702],{"content":678,"config":689},{"body":679,"title":680,"description":681,"authors":682,"heroImage":684,"date":685,"category":9,"tags":686},"## 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/ee/api/jobs.html#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.",[683],"Omid Khan","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772721753/frfsm1qfscwrmsyzj1qn.png","2026-04-09",[23,687,25,688],"DevOps platform","features",{"featured":12,"template":13,"slug":690},"5-ways-gitlab-pipeline-logic-solves-real-engineering-problems",{"content":692,"config":700},{"title":693,"description":694,"authors":695,"heroImage":696,"date":697,"body":698,"category":9,"tags":699},"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.",[18],"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.",[25,27,688],{"featured":34,"template":13,"slug":701},"using-gitlab-container-virtual-registry-with-docker-hardened-images",{"content":703,"config":714},{"category":9,"tags":704,"body":706,"date":707,"heroImage":708,"authors":709,"title":712,"description":713},[25,705,23],"git","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/ee/user/permissions.html) 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","2025-12-03","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png",[710,711],"Evgeny Rudinsky","Michael Leopard","Migration von Azure DevOps zu GitLab systematisch planen","Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.",{"featured":12,"template":13,"slug":715},"migration-from-azure-devops-to-gitlab",{"promotions":717},[718,732,744,756],{"id":719,"categories":720,"header":722,"text":723,"button":724,"image":729},"ai-modernization",[721],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":725,"config":726},"Get your AI maturity score",{"href":727,"dataGaName":728,"dataGaLocation":249},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":730},{"src":731},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":733,"categories":734,"header":736,"text":723,"button":737,"image":741},"devops-modernization",[27,735],"devsecops","Are you just managing tools or shipping innovation?",{"text":738,"config":739},"Get your DevOps maturity score",{"href":740,"dataGaName":728,"dataGaLocation":249},"/assessments/devops-modernization-assessment/",{"config":742},{"src":743},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":745,"categories":746,"header":748,"text":723,"button":749,"image":753},"security-modernization",[747],"security","Are you trading speed for security?",{"text":750,"config":751},"Get your security maturity score",{"href":752,"dataGaName":728,"dataGaLocation":249},"/assessments/security-modernization-assessment/",{"config":754},{"src":755},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":757,"paths":758,"header":760,"text":761,"button":762,"image":767},"github-azure-migration",[715,759],"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":763,"config":764},"See how GitLab compares to GitHub",{"href":765,"dataGaName":766,"dataGaLocation":249},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":768},{"src":743},{"header":770,"blurb":771,"button":772,"secondaryButton":777},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":773,"config":774},"Kostenlosen Test starten",{"href":775,"dataGaName":56,"dataGaLocation":776},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/de-de/","feature",{"text":58,"config":778},{"href":60,"dataGaName":61,"dataGaLocation":776},1776449917503]