[{"data":1,"prerenderedAt":771},["ShallowReactive",2],{"/de-de/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake":3,"navigation-de-de":37,"banner-de-de":440,"footer-de-de":450,"blog-post-authors-de-de-Radovan Bacovic":655,"blog-related-posts-de-de-how-we-built-a-structured-streamlit-application-framework-in-snowflake":669,"blog-promotions-de-de":709,"next-steps-de-de":761},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":27,"isFeatured":11,"meta":28,"navigation":29,"path":30,"publishedDate":20,"seo":31,"stem":34,"tagSlugs":35,"__hash__":36},"blogPosts/de-de/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake.yml","How We Built A Structured Streamlit Application Framework In Snowflake",[7],"radovan-bacovic",null,"engineering",{"featured":11,"template":12,"slug":13},false,"BlogPost","how-we-built-a-structured-streamlit-application-framework-in-snowflake",{"title":15,"description":16,"authors":17,"heroImage":19,"date":20,"body":21,"category":9,"tags":22},"Streamlit-Framework: Systematische Infrastruktur-Governance","Das GitLab Data Team zeigt, wie frühzeitige Infrastruktur-Governance exponentiell steigende Wartungskosten verhindert. Ein systematischer Ansatz für Streamlit-Anwendungen in regulierten Umgebungen.",[18],"Radovan Bacovic","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097447/Blog/Hero%20Images/Blog/Hero%20Images/blog-image-template-1800x945%20%284%29_3LZkiDjHLjhqEkvOvBsVKp_1750097447404.png","2025-10-20","Das GitLab Data Team hat dutzende isolierte [Streamlit](https://streamlit.io/)-Anwendungen in eine einheitliche, sichere und skalierbare Lösung für die Snowflake-Umgebung überführt. Die Kombination von Python, Snowflake, Streamlit und GitLab ermöglicht systematische Infrastrukturkontrolle. Dieser Beitrag beschreibt den methodischen Ansatz und die erzielten Ergebnisse.\n## Die Ausgangssituation\nDutzende Streamlit-Anwendungen in verschiedenen Umgebungen, unterschiedliche Python-Versionen, inkonsistente Sicherheitspraktiken beim Zugriff auf sensible Daten. Manche Anwendungen funktionieren, andere versagen ohne nachvollziehbaren Grund. Niemand weiß, wer welche Anwendung erstellt hat oder wie sie zu warten ist.\nGenau vor dieser Situation stand unser Data Team. Anwendungen entstanden isoliert, ohne Standardisierung, ohne Security-Oversight, ohne klaren Deployment-Prozess. Das Ergebnis: ein Compliance-Risiko und eine Wartungslast, die exponentiell wuchs.\n\n![Funktionale Architektur (High-Level-Übersicht)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/i50lpkrwy9bok056rdak.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Funktionale Architektur (High-Level-Übersicht)\u003C/i>\u003C/center>\n## Der methodische Ansatz\nAls \"Customer Zero\" haben wir das gesamte Framework auf GitLabs eigener CI/CD-Infrastruktur und den Projekt-Management-Tools aufgebaut. Die Grundkomponenten:\n1. [GitLab](https://about.gitlab.com/platform/) (Produkt)\n1. [Snowflake](https://about.gitlab.com/platform/) – Single Source of Truth (SSOT) für Data-Warehouse-Aktivitäten\n1. [Streamlit](https://streamlit.io/) – Open-Source-Tool für visuelle Anwendungen mit Python-Code\nDies ermöglichte direkten Zugriff auf Enterprise-DevSecOps-Funktionen: automatisierte Tests, Code-Review-Prozesse und Deployment-Pipelines von Beginn an. Durch GitLabs integrierte Features für Issue-Tracking, Merge-Requests und automatisierte Deployments (CI/CD-Pipelines) konnten wir schnell iterieren und das Framework gegen reale Enterprise-Anforderungen validieren. Dieser Internal-First-Ansatz stellte sicher, dass die Lösung im Produktiveinsatz bei GitLab selbst validiert wurde.\n### Erkenntnisse aus der Implementierung\nDie wichtigste Erkenntnis beim Aufbau des Streamlit Application Framework in Snowflake: **Struktur schlägt Chaos systematisch** – Governance früh implementieren, nicht nachträglich, wenn die Wartungskosten exponentiell steigen.\nRollen und Verantwortlichkeiten müssen klar definiert sein. Infrastruktur-Concerns werden von Application Development getrennt, sodass jedes Team sich auf seine Stärken konzentrieren kann.\nSecurity und Compliance können keine Nachgedanken sein. Sie müssen von Tag eins in Templates und automatisierte Prozesse integriert werden. Konsistente Standards vorab durchzusetzen ist wesentlich effizienter als nachträgliche Implementierung. Investitionen in Automatisierung und CI/CD-Pipelines zahlen sich aus, da manuelle Prozesse nicht skalieren und menschliche Fehler einführen.\n![Framework-Architektur (Gesamtübersicht)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035998/qt9gfemxjnj8kjumkuh7.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Framework-Architektur (Gesamtübersicht)\u003C/i>\u003C/center>\n## Das Streamlit Application Framework\nDas Framework verwandelt dezentrale Ansätze in eine strukturierte Lösung. Entwicklungsteams erhalten Freiheit innerhalb sicherer Leitplanken, während Deployment automatisiert und Wartungskomplexität eliminiert wird.\n### Drei Rollen, ein einheitlicher Prozess\nDas Framework führt einen strukturierten Ansatz mit drei klar getrennten Rollen ein:\n1. **Maintainers** (Data-Team-Mitglieder und Contributors) verwalten die Infrastruktur: CI/CD-Pipelines, Security-Templates und Compliance-Regeln. Sie stellen sicher, dass das Framework funktioniert und sicher bleibt.\n2. **Creators** (Anwendungsentwicklungsteams) konzentrieren sich auf ihre Kernkompetenzen: Visualisierungen erstellen, Snowflake-Daten einbinden, User Experiences gestalten. Volle Flexibilität beim Erstellen neuer Anwendungen von Grund auf, beim Hinzufügen neuer Pages zu bestehenden Apps, beim Integrieren zusätzlicher Python-Libraries und beim Bauen komplexer Datenvisualisierungen, ohne Beschäftigung mit Deployment-Pipelines oder Security-Konfigurationen.\n3. **Viewers** (Endnutzer) greifen auf fertige, sichere Anwendungen zu, ohne technischen Overhead. Benötigt wird lediglich Snowflake-Zugriff.\n![Rollen-Übersicht und ihre Funktionen](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/oatqyx3ug7vsgzishpma.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Rollen-Übersicht und ihre Funktionen\u003C/i>\u003C/center>\n## Vollständige Automatisierung\nDurch CI/CD-Implementierung gehören tagelange manuelle Deployments und Konfigurationsaufwand der Vergangenheit an. Das Framework bietet:\n- **Umgebungsvorbereitung per Kommando:** Mit `make`-Befehlen ist die Umgebung in wenigen Sekunden installiert und einsatzbereit.\n```yaml ================================================================================ ✅ Snowflake CLI successfully installed and configured! Connection: gitlab_streamlit User: YOU@GITLAB.COM Account: gitlab ================================================================================ Using virtualenv: /Users/YOU/repos/streamlit/.venv 📚 Installing project dependencies... Installing dependencies from lock file No dependencies to install or update ✅ Streamlit environment prepared! ```\n- **Automatisierte CI/CD-Pipelines:** Übernehmen Testing, Code-Review und Deployment von Development bis Production.\n- **Sichere Sandbox-Umgebungen:** Ermöglichen sichere Entwicklung und Tests vor Production-Deployment.\n```yaml ╰─$ make streamlit-rules 🔍 Running Streamlit compliance check... ================================================================================ CODE COMPLIANCE REPORT ================================================================================ Generated: 2025-07-09 14:01:16 Files checked: 1\nSUMMARY: ✅ Passed: 1 ❌ Failed: 0 Success Rate: 100.0%\nAPPLICATION COMPLIANCE SUMMARY: 📱 Total Applications Checked: 1 ⚠️ Applications with Issues: 0 📊 File Compliance Rate: 100.0%\nDETAILED RESULTS BY APPLICATION: ... ```\n- **Template-basierte Anwendungserstellung:** Gewährleistet Konsistenz über alle Anwendungen und Pages hinweg.\n```shell ╰─$ make streamlit-new-page STREAMLIT_APP=sales_dashboard STREAMLIT_PAGE_NAME=analytics 📝 Generating new Streamlit page: analytics for app: sales_dashboard 📃 Create new page from template: Page name: analytics App directory: sales_dashboard Template path: page_template.py ✅ Successfully created 'analytics.py' in 'sales_dashboard' directory from template ```\n- **Poetry-basiertes Dependency-Management:** Verhindert Versionskonflikte und erhält saubere Umgebungen.\n- **Organisierte Projektstruktur:** Dedizierte Ordner für Anwendungen, Templates, Compliance-Regeln und Configuration-Management.\n```text ├── src/ │   ├── applications/     # Ordner für Streamlit-Anwendungen │   │   ├── main_app/     # Main-Dashboard-Anwendung │   │   ├── components/   # Gemeinsam genutzte Komponenten │   │   └── \u003Cyour_apps>/  # Eigene Anwendungen │   │   └── \u003Cyour_apps2>/ # Weitere Anwendungen │   ├── templates/        # Anwendungs- und Page-Templates │   ├── compliance/       # Compliance-Regeln und -Checks │   └── setup/            # Setup- und Konfigurations-Utilities ├── tests/                # Test-Dateien ├── config.yml            # Umgebungskonfiguration ├── Makefile              # Build- und Deployment-Automatisierung └── README.md             # Haupt-README-Datei ```\n- **Optimierter Workflow:** Von lokaler Entwicklung über Test-Schema bis Production, vollständig automatisiert durch GitLab CI/CD-Pipelines.\n![GitLab CI/CD-Pipelines für vollständige Prozessautomatisierung](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035998/usyma2jkgiazu9iay1au.png)\n\u003Cp>\u003C/p> \u003Ccenter>\u003Ci>GitLab CI/CD-Pipelines für vollständige Prozessautomatisierung\u003C/i>\u003C/center>\n## Security und Compliance by Design\nStatt Security nachträglich hinzuzufügen, baut das Streamlit Application Framework sie von Grund auf ein. Jede Anwendung folgt denselben Security-Standards, Compliance-Anforderungen werden automatisch durchgesetzt. Audit-Trails werden über den gesamten Development-Lifecycle gepflegt.\nCompliance-Regeln werden mit einem einzigen Kommando eingeführt und verifiziert. Beispielsweise lassen sich definieren, welche Klassen und Methoden verpflichtend sind, welche Dateien vorhanden sein müssen und welche Rollen für das Teilen der Anwendung erlaubt oder verboten sind. Die Regeln sind flexibel und beschreibend – Definition erfolgt in einer YAML-Datei:\n```yaml class_rules:\n  - name: \"Inherit code for the page from GitLabDataStreamlitInit\"\n    description: \"All Streamlit apps must inherit from GitLabDataStreamlitInit\"\n    severity: \"error\"\n    required: true\n    class_name: \"*\"\n    required_base_classes:\n      - \"GitLabDataStreamlitInit\"\n    required_methods:\n      - \"__init__\"\n      - \"set_page_layout\"\n      - \"setup_ui\"\n      - \"run\"\n\nfunction_rules:\n  - name: \"Main function required\"\n    description: \"Must have a main() function\"\n    severity: \"error\"\n    required: true\n    function_name: \"main\"\n\nimport_rules:\n  - name: \"Import GitLabDataStreamlitInit\"\n    description: \"Must import the mandatory base class\"\n    severity: \"error\"\n    required: true\n    module_name: \"gitlab_data_streamlit_init\"\n    required_items:\n      - \"GitLabDataStreamlitInit\"\n  - name: \"Import streamlit\"\n    description: \"Must import streamlit library\"\n    severity: \"error\"\n    required: true\n    module_name: \"streamlit\"\n\nfile_rules:\n  - name: \"Snowflake configuration required (snowflake.yml)\"\n    description: \"Each application must have a snowflake.yml configuration file\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/snowflake.yml\"\n    base_path: \"\"\n  - name: \"Snowflake environment required (environment.yml)\"\n    description: \"Each application must have a environment.yml configuration file\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/environment.yml\"\n    base_path: \"\"\n  - name: \"Share specification required (share.yml)\"\n    description: \"Each application must have a share.yml file\"\n    severity: \"warning\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    base_path: \"\"\n  - name: \"README.md required (README.md)\"\n    description: \"Each application should have a README.md file with a proper documentation\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/README.md\"\n    base_path: \"\"\n  - name: \"Starting point recommended (dashboard.py)\"\n    description: \"Each application must have a dashboard.py as a starting point\"\n    severity: \"warning\"\n    required: true\n    file_pattern: \"**/applications/**/dashboard.py\"\n    base_path: \"\"\n\nsql_rules:\n  - name: \"SQL files must contain only SELECT statements\"\n    description: \"SQL files and SQL code in other files should only contain SELECT statements for data safety\"\n    severity: \"error\"\n    required: true\n    file_extensions: [\".sql\", \".py\"]\n    select_only: true\n    forbidden_statements:\n      - ....\n    case_sensitive: false\n  - name: \"SQL queries should include proper SELECT statements\"\n    description: \"When SQL is present, it should contain proper SELECT statements\"\n    severity: \"warning\"\n    required: false\n    file_extensions: [\".sql\", \".py\"]\n    required_statements:\n      - \"SELECT\"\n    case_sensitive: false\n\nshare_rules:\n  - name: \"Valid functional roles in share.yml\"\n    description: \"Share.yml files must contain only valid functional roles from the approved list\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    valid_roles:\n      - ...\n    safe_data_roles:\n      - ...\n  - name: \"Share.yml file format validation\"\n    description: \"Share.yml files must follow the correct YAML format structure\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    required_keys:\n      - \"share\"\n    min_roles: 1\n    max_roles: 10\n```\nMit einem einzigen Kommando:\n```bash ╰─$ make streamlit-rules ```\nlassen sich alle erstellten Regeln verifizieren und validieren, dass Entwicklungsteams (die eine Streamlit-Anwendung erstellen) die von den Creators (die Policies und Building Blocks des Frameworks festlegen) spezifizierten Richtlinien befolgen und alle Building Blocks an der richtigen Stelle sind. Dies gewährleistet konsistentes Verhalten über alle Streamlit-Anwendungen hinweg.\n```yaml 🔍 Running Streamlit compliance check... ================================================================================ CODE COMPLIANCE REPORT ================================================================================ Generated: 2025-08-18 17:05:12 Files checked: 4\nSUMMARY: ✅ Passed: 4 ❌ Failed: 0 Success Rate: 100.0%\nAPPLICATION COMPLIANCE SUMMARY: 📱 Total Applications Checked: 1 ⚠️ Applications with Issues: 0 📊 File Compliance Rate: 100.0%\nDETAILED RESULTS BY APPLICATION: ================================================================================ ✅ PASS APPLICATION: main_app ------------------------------------------------------------ 📁 FILES ANALYZED (4): ✅ dashboard.py 📦 Classes: SnowflakeConnectionTester 🔧 Functions: main 📥 Imports: os, pwd, gitlab_data_streamlit_init, snowflake.snowpark.exceptions, streamlit\n✅ show_streamlit_apps.py 📦 Classes: ShowStreamlitApps 🔧 Functions: main 📥 Imports: pandas, gitlab_data_streamlit_init, snowflake_session, streamlit\n✅ available_packages.py 📦 Classes: AvailablePackages 🔧 Functions: main 📥 Imports: pandas, gitlab_data_streamlit_init, streamlit\n✅ share.yml 👥 Share Roles: snowflake_analyst_safe\n📄 FILE COMPLIANCE FOR MAIN_APP: ✅ Required files found: ✓ snowflake.yml ✓ environment.yml ✓ share.yml ✓ README.md ✓ dashboard.py\nRULES CHECKED: ---------------------------------------- Class Rules (1): - Inherit code for the page from GitLabDataStreamlitInit (error)\nFunction Rules (1): - Main function required (error)\nImport Rules (2): - Import GitLabDataStreamlitInit (error) - Import streamlit (error)\nFile Rules (5): - Snowflake configuration required (snowflake.yml) (error) - Snowflake environment required (environment.yml) (error) - Share specification required (share.yml) (warning) - README.md required (README.md) (error) - Starting point recommended (dashboard.py) (warning)\nSQL Rules (2): - SQL files must contain only SELECT statements (error) 🗄 SELECT-only mode enabled 🚨 Forbidden: INSERT, UPDATE, DELETE, DROP, ALTER... - SQL queries should include proper SELECT statements (warning)\nShare Rules (2): - Valid functional roles in share.yml (error) 👥 Valid roles: 15 roles defined 🔒 Safe data roles: 11 roles - Share.yml file format validation (error) ------------------------------------------------------------ ✅ Compliance check passed ----------------------------------------------------------- ```\n## Developer Experience\nOb bevorzugte IDE, webbasierte Entwicklungsumgebung oder Snowflake Snowsight – die Experience bleibt konsistent. Das Framework bietet:\n- **Template-basierte Entwicklung:** Neue Anwendungen und Pages werden über standardisierte Templates erstellt, was Konsistenz und Best Practices von Tag eins sicherstellt. Keine verstreuten Designs und Elemente mehr.\n```yaml ╰─$ make streamlit-new-app NAME=sales_dashboard 🔧 Configuration Environment: TEST 📝 Configuration File: config.yml 📜 Config Loader Script: ./setup/get_config.sh 🐍 Python Version: 3.12 📁 Applications Directory: ./src/applications 🗄 Database: ... 📊 Schema: ... 🏗 Stage: ... 🏭 Warehouse: ... 🆕 Creating new Streamlit app: sales_dashboard Initialized the new project in ./src/applications/sales_dashboard ```\n- **Poetry Package Management:** Alle Dependencies werden über Poetry verwaltet, was isolierte Umgebungen schafft, die bestehende Python-Setups nicht stören.\n```toml [tool.poetry] name = \"GitLab Data Streamlit\" version = \"0.1.1\" description = \"GitLab Data Team Streamlit project\" authors = [\"GitLab Data Team \u003C*****@gitlab.com>\"] readme = \"README.md\"\n[tool.poetry.dependencies] python = \"\u003C3.13,>=3.12\" snowflake-snowpark-python = \"==1.32.0\" snowflake-connector-python = {extras = [\"development\", \"pandas\", \"secure-local-storage\"], version = \"^3.15.0\"} streamlit = \"==1.22.0\" watchdog = \"^6.0.0\" types-toml = \"^0.10.8.20240310\" pytest = \"==7.0.0\" black = \"==25.1.0\" importlib-metadata = \"==4.13.0\" pyyaml = \"==6.0.2\" python-qualiter = \"*\" ruff = \"^0.1.0\" types-pyyaml = \"^6.0.12.20250516\" jinja2 = \"==3.1.6\"\n[build-system] requires = [\"poetry-core\"] build-backend = \"poetry.core.masonry.api\" ```\n- **Multi-Page-Application-Support:** Teams können problemlos komplexe Anwendungen mit mehreren Pages erstellen und neue Libraries nach Bedarf hinzufügen. Multi-Page-Anwendungen sind Teil des Frameworks – Fokus liegt auf der Logik, nicht auf Design und Strukturierung.\n![Multi-Page-Anwendungsbeispiel (in Snowflake)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/at1q2xgmjthkrgju4okm.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Multi-Page-Anwendungsbeispiel (in Snowflake)\u003C/i>\u003C/center>\n\u003Cp>\u003C/p>\n- **Nahtlose Snowflake-Integration:** Integrierte Konnektoren und Authentication-Handling für sicheren Datenzugriff bieten dieselbe Experience in lokaler Entwicklung und direkt in Snowflake.\n```yaml make streamlit-push-test APPLICATION_NAME=sales_dashboard 📤 Deploying Streamlit app to test environment: sales_dashboard ... ------------------------------------------------------------------------------------------------------------ 🔗 Running share command for application: sales_dashboard Running commands to grant shares 🚀 Executing: snow streamlit share sales_dashboard with SOME_NICE_ROLE ✅ Command executed successfully 📊 Execution Summary: 1/1 commands succeeded ```\n- **Umfassendes Makefile:** Alle gängigen Kommandos sind in einfache Makefile-Befehle verpackt, von lokaler Entwicklung über Testing bis Deployment, inklusive CI/CD-Pipelines.\n- **Sichere lokale Entwicklung:** Alles läuft in isolierten Poetry-Umgebungen, schützt das System und bietet Production-ähnliche Experiences.\n![Konsistente Experience unabhängig von der Umgebung (Beispiel lokale Entwicklung)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/phmubsb34hn2mfefjvqh.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Konsistente Experience unabhängig von der Umgebung (Beispiel lokale Entwicklung)\u003C/i>\u003C/center>\n\u003Cp>\u003C/p>\n- **Collaboration via Code:** Alle Anwendungen und Komponenten sind in einem Repository zusammengefasst, was der gesamten Organisation ermöglicht, an denselben Ressourcen zu kollaborieren und doppelte Arbeit sowie redundante Setups zu vermeiden.\n## Implementierungsschritte\nBei ähnlichen Herausforderungen mit verstreuten Streamlit-Anwendungen:\n1. **Bestandsaufnahme:** Bestehende Anwendungen inventarisieren und Problembereiche identifizieren.\n2. **Rollen definieren:** Maintainer-Verantwortlichkeiten von Creator- und Endnutzer-Anforderungen trennen.\n3. **Mit Templates beginnen:** Standardisierte Anwendungs-Templates erstellen, die Security- und Compliance-Anforderungen durchsetzen.\n4. **CI/CD implementieren:** Deployment-Pipeline automatisieren, um manuelle Fehler zu reduzieren und Konsistenz sicherzustellen.\n![Die in Snowflake deployte Anwendung](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760036003/mzge9s1fhkhnx38y1a3i.png)\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>Die in Snowflake deployte Anwendung\u003C/i>\u003C/center>\n## Einordnung\nDieses Framework behandelt Daten-Anwendungen als vollwertige Komponenten der Enterprise-Architektur.\nDurch die Bereitstellung von Struktur ohne Flexibilitätsverlust hat das GitLab Data Team eine Umgebung geschaffen, in der Teams mit minimalen technischen Vorkenntnissen schnell innovieren können, während höchste Security- und Compliance-Standards gewahrt bleiben.\n### Ausblick\nWir entwickeln das Framework basierend auf User-Feedback und entstehenden Anforderungen kontinuierlich weiter. Zukünftige Verbesserungen umfassen erweiterte Template-Libraries, verbesserte Monitoring-Funktionen, mehr Flexibilität und eine optimierte User Experience.\n**Das Ziel: ein Foundation schaffen, das mit den wachsenden Data-Application-Anforderungen der Organisation skaliert.**\nWeitere technische Details zur Implementierung im [englischen Original](https://about.gitlab.com/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake/).\n## Zusammenfassung\nDas [GitLab Data Team](https://handbook.gitlab.com/handbook/enterprise-data/) hat dutzende verstreute, unsichere Streamlit-Anwendungen ohne Standardisierung in ein einheitliches, Enterprise-taugliches Framework mit klarer Rollentrennung überführt:\n1. **Maintainers** verwalten Infrastruktur und Security.\n2. **Creators** konzentrieren sich auf Anwendungsentwicklung ohne Deployment-Overhead.\n3. **Viewers** greifen auf fertige, compliance-konforme Apps zu.\nDie verwendeten Building Blocks:\n1. Automatisierte **CI/CD**-Pipelines\n2. Vollständig kollaborativer und versionierter Code in **git**\n3. **Template-basierte** Entwicklung\n4. Integrierte **Security**-Compliance und Testing\n5. **Poetry-verwaltete** Umgebungen\nWir haben den Wartungs-Overhead eliminiert und gleichzeitig schnelle Innovation ermöglicht – der Beweis, dass Struktur und Flexibilität vereinbar sind, wenn Data Applications als vollwertige Enterprise-Assets behandelt werden, nicht als Wegwerf-Prototypen.\n",[23,24,25,26],"product","features","security","tutorial","yml",{},true,"/de-de/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake",{"config":32,"title":15,"ogTitle":15,"description":33},{"noIndex":11},"Das GitLab Data Team zeigt, wie frühzeitige Infrastruktur-Governance Wartungskosten verhindert. Systematischer Ansatz für Streamlit in regulierten Umgebungen.","de-de/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake",[23,24,25,26],"rSSLCWThl6CuiOKggcjBwddeGgQjVQBdAA0S9KKe0mw",{"data":38},{"logo":39,"freeTrial":44,"sales":49,"login":54,"items":59,"search":368,"minimal":403,"duo":421,"pricingDeployment":430},{"config":40},{"href":41,"dataGaName":42,"dataGaLocation":43},"/de-de/","gitlab logo","header",{"text":45,"config":46},"Kostenlose Testversion anfordern",{"href":47,"dataGaName":48,"dataGaLocation":43},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de&glm_content=default-saas-trial/","free trial",{"text":50,"config":51},"Vertrieb kontaktieren",{"href":52,"dataGaName":53,"dataGaLocation":43},"/de-de/sales/","sales",{"text":55,"config":56},"Anmelden",{"href":57,"dataGaName":58,"dataGaLocation":43},"https://gitlab.com/users/sign_in/","sign in",[60,87,183,188,289,349],{"text":61,"config":62,"cards":64},"Plattform",{"dataNavLevelOne":63},"platform",[65,71,79],{"title":61,"description":66,"link":67},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":68,"config":69},"Erkunde unsere Plattform",{"href":70,"dataGaName":63,"dataGaLocation":43},"/de-de/platform/",{"title":72,"description":73,"link":74},"GitLab Duo Agent Platform","Agentische KI für den gesamten Softwareentwicklungszyklus",{"text":75,"config":76},"Lerne GitLab Duo kennen",{"href":77,"dataGaName":78,"dataGaLocation":43},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":80,"description":81,"link":82},"Gründe, die für GitLab sprechen","Erfahre, warum Unternehmen auf GitLab setzen",{"text":83,"config":84},"Mehr erfahren",{"href":85,"dataGaName":86,"dataGaLocation":43},"/de-de/why-gitlab/","why gitlab",{"text":88,"left":29,"config":89,"link":91,"lists":95,"footer":165},"Produkt",{"dataNavLevelOne":90},"solutions",{"text":92,"config":93},"Alle Lösungen anzeigen",{"href":94,"dataGaName":90,"dataGaLocation":43},"/de-de/solutions/",[96,121,143],{"title":97,"description":98,"link":99,"items":104},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":100},{"icon":101,"href":102,"dataGaName":103,"dataGaLocation":43},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[105,109,112,117],{"text":106,"config":107},"CI/CD",{"href":108,"dataGaLocation":43,"dataGaName":106},"/de-de/solutions/continuous-integration/",{"text":72,"config":110},{"href":77,"dataGaLocation":43,"dataGaName":111},"gitlab duo agent platform - product menu",{"text":113,"config":114},"Quellcodeverwaltung",{"href":115,"dataGaLocation":43,"dataGaName":116},"/de-de/solutions/source-code-management/","Source Code Management",{"text":118,"config":119},"Automatisierte Softwarebereitstellung",{"href":102,"dataGaLocation":43,"dataGaName":120},"Automated software delivery",{"title":122,"description":123,"link":124,"items":129},"Sicherheit","Entwickle schneller, ohne die Sicherheit zu gefährden",{"config":125},{"href":126,"dataGaName":127,"dataGaLocation":43,"icon":128},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[130,134,139],{"text":131,"config":132},"Application Security Testing",{"href":126,"dataGaName":133,"dataGaLocation":43},"Application security testing",{"text":135,"config":136},"Schutz der Software-Lieferkette",{"href":137,"dataGaLocation":43,"dataGaName":138},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":140,"config":141},"Software Compliance",{"href":142,"dataGaName":140,"dataGaLocation":43},"/de-de/solutions/software-compliance/",{"title":144,"link":145,"items":150},"Bewertung",{"config":146},{"icon":147,"href":148,"dataGaName":149,"dataGaLocation":43},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[151,155,160],{"text":152,"config":153},"Sichtbarkeit und Bewertung",{"href":148,"dataGaLocation":43,"dataGaName":154},"Visibility and Measurement",{"text":156,"config":157},"Wertstrommanagement",{"href":158,"dataGaLocation":43,"dataGaName":159},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":161,"config":162},"Analysen und Einblicke",{"href":163,"dataGaLocation":43,"dataGaName":164},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":166,"items":167},"GitLab für",[168,173,178],{"text":169,"config":170},"Enterprise",{"href":171,"dataGaLocation":43,"dataGaName":172},"/de-de/enterprise/","enterprise",{"text":174,"config":175},"Kleinunternehmen",{"href":176,"dataGaLocation":43,"dataGaName":177},"/de-de/small-business/","small business",{"text":179,"config":180},"den öffentlichen Sektor",{"href":181,"dataGaLocation":43,"dataGaName":182},"/de-de/solutions/public-sector/","public sector",{"text":184,"config":185},"Preise",{"href":186,"dataGaName":187,"dataGaLocation":43,"dataNavLevelOne":187},"/de-de/pricing/","pricing",{"text":189,"config":190,"link":192,"lists":196,"feature":276},"Ressourcen",{"dataNavLevelOne":191},"resources",{"text":193,"config":194},"Alle Ressourcen anzeigen",{"href":195,"dataGaName":191,"dataGaLocation":43},"/de-de/resources/",[197,230,248],{"title":198,"items":199},"Erste Schritte",[200,205,210,215,220,225],{"text":201,"config":202},"Installieren",{"href":203,"dataGaName":204,"dataGaLocation":43},"/de-de/install/","install",{"text":206,"config":207},"Kurzanleitungen",{"href":208,"dataGaName":209,"dataGaLocation":43},"/de-de/get-started/","quick setup checklists",{"text":211,"config":212},"Lernen",{"href":213,"dataGaLocation":43,"dataGaName":214},"https://university.gitlab.com/","learn",{"text":216,"config":217},"Produktdokumentation",{"href":218,"dataGaName":219,"dataGaLocation":43},"https://docs.gitlab.com/","product documentation",{"text":221,"config":222},"Best-Practice-Videos",{"href":223,"dataGaName":224,"dataGaLocation":43},"/de-de/getting-started-videos/","best practice videos",{"text":226,"config":227},"Integrationen",{"href":228,"dataGaName":229,"dataGaLocation":43},"/de-de/integrations/","integrations",{"title":231,"items":232},"Entdecken",[233,238,243],{"text":234,"config":235},"Kundenerfolge",{"href":236,"dataGaName":237,"dataGaLocation":43},"/de-de/customers/","customer success stories",{"text":239,"config":240},"Blog",{"href":241,"dataGaName":242,"dataGaLocation":43},"/de-de/blog/","blog",{"text":244,"config":245},"Remote",{"href":246,"dataGaName":247,"dataGaLocation":43},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":249,"items":250},"Vernetzen",[251,256,261,266,271],{"text":252,"config":253},"GitLab-Services",{"href":254,"dataGaName":255,"dataGaLocation":43},"/de-de/services/","services",{"text":257,"config":258},"Community",{"href":259,"dataGaName":260,"dataGaLocation":43},"/community/","community",{"text":262,"config":263},"Forum",{"href":264,"dataGaName":265,"dataGaLocation":43},"https://forum.gitlab.com/","forum",{"text":267,"config":268},"Veranstaltungen",{"href":269,"dataGaName":270,"dataGaLocation":43},"/events/","events",{"text":272,"config":273},"Partner",{"href":274,"dataGaName":275,"dataGaLocation":43},"/de-de/partners/","partners",{"backgroundColor":277,"textColor":278,"text":279,"image":280,"link":284},"#2f2a6b","#fff","Perspektiven für die Softwareentwicklung der Zukunft",{"altText":281,"config":282},"the source promo card",{"src":283},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":285,"config":286},"Lies die News",{"href":287,"dataGaName":288,"dataGaLocation":43},"/de-de/the-source/","the source",{"text":290,"config":291,"lists":293},"Unternehmen",{"dataNavLevelOne":292},"company",[294],{"items":295},[296,301,307,309,314,319,324,329,334,339,344],{"text":297,"config":298},"Über",{"href":299,"dataGaName":300,"dataGaLocation":43},"/de-de/company/","about",{"text":302,"config":303,"footerGa":306},"Karriere",{"href":304,"dataGaName":305,"dataGaLocation":43},"/jobs/","jobs",{"dataGaName":305},{"text":267,"config":308},{"href":269,"dataGaName":270,"dataGaLocation":43},{"text":310,"config":311},"Geschäftsführung",{"href":312,"dataGaName":313,"dataGaLocation":43},"/company/team/e-group/","leadership",{"text":315,"config":316},"Team",{"href":317,"dataGaName":318,"dataGaLocation":43},"/company/team/","team",{"text":320,"config":321},"Handbuch",{"href":322,"dataGaName":323,"dataGaLocation":43},"https://handbook.gitlab.com/","handbook",{"text":325,"config":326},"Investor Relations",{"href":327,"dataGaName":328,"dataGaLocation":43},"https://ir.gitlab.com/","investor relations",{"text":330,"config":331},"Trust Center",{"href":332,"dataGaName":333,"dataGaLocation":43},"/de-de/security/","trust center",{"text":335,"config":336},"AI Transparency Center",{"href":337,"dataGaName":338,"dataGaLocation":43},"/de-de/ai-transparency-center/","ai transparency center",{"text":340,"config":341},"Newsletter",{"href":342,"dataGaName":343,"dataGaLocation":43},"/company/contact/#contact-forms","newsletter",{"text":345,"config":346},"Presse",{"href":347,"dataGaName":348,"dataGaLocation":43},"/press/","press",{"text":350,"config":351,"lists":352},"Kontakt",{"dataNavLevelOne":292},[353],{"items":354},[355,358,363],{"text":50,"config":356},{"href":52,"dataGaName":357,"dataGaLocation":43},"talk to sales",{"text":359,"config":360},"Support-Portal",{"href":361,"dataGaName":362,"dataGaLocation":43},"https://support.gitlab.com","support portal",{"text":364,"config":365},"Kundenportal",{"href":366,"dataGaName":367,"dataGaLocation":43},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":369,"login":370,"suggestions":377},"Schließen",{"text":371,"link":372},"Um Repositories und Projekte zu durchsuchen, melde dich an bei",{"text":373,"config":374},"gitlab.com",{"href":57,"dataGaName":375,"dataGaLocation":376},"search login","search",{"text":378,"default":379},"Vorschläge",[380,382,387,389,394,399],{"text":72,"config":381},{"href":77,"dataGaName":72,"dataGaLocation":376},{"text":383,"config":384},"Code Suggestions (KI)",{"href":385,"dataGaName":386,"dataGaLocation":376},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":106,"config":388},{"href":108,"dataGaName":106,"dataGaLocation":376},{"text":390,"config":391},"GitLab auf AWS",{"href":392,"dataGaName":393,"dataGaLocation":376},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":395,"config":396},"GitLab auf Google Cloud",{"href":397,"dataGaName":398,"dataGaLocation":376},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":400,"config":401},"Warum GitLab?",{"href":85,"dataGaName":402,"dataGaLocation":376},"Why GitLab?",{"freeTrial":404,"mobileIcon":409,"desktopIcon":414,"secondaryButton":417},{"text":405,"config":406},"Kostenlos testen",{"href":407,"dataGaName":48,"dataGaLocation":408},"https://gitlab.com/-/trials/new/","nav",{"altText":410,"config":411},"GitLab-Symbol",{"src":412,"dataGaName":413,"dataGaLocation":408},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":410,"config":415},{"src":416,"dataGaName":413,"dataGaLocation":408},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":198,"config":418},{"href":419,"dataGaName":420,"dataGaLocation":408},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/get-started/","get started",{"freeTrial":422,"mobileIcon":426,"desktopIcon":428},{"text":423,"config":424},"Erfahre mehr über GitLab Duo",{"href":77,"dataGaName":425,"dataGaLocation":408},"gitlab duo",{"altText":410,"config":427},{"src":412,"dataGaName":413,"dataGaLocation":408},{"altText":410,"config":429},{"src":416,"dataGaName":413,"dataGaLocation":408},{"freeTrial":431,"mobileIcon":436,"desktopIcon":438},{"text":432,"config":433},"Zurück zur Preisübersicht",{"href":186,"dataGaName":434,"dataGaLocation":408,"icon":435},"back to pricing","GoBack",{"altText":410,"config":437},{"src":412,"dataGaName":413,"dataGaLocation":408},{"altText":410,"config":439},{"src":416,"dataGaName":413,"dataGaLocation":408},{"title":441,"button":442,"config":447},"Sieh dir an, wie agentische KI die Softwarebereitstellung transformiert",{"text":443,"config":444},"GitLab Transcend jetzt ansehen",{"href":445,"dataGaName":446,"dataGaLocation":43},"/de-de/events/transcend/virtual/","transcend event",{"layout":448,"icon":449,"disabled":29},"release","AiStar",{"data":451},{"text":452,"source":453,"edit":459,"contribute":464,"config":469,"items":474,"minimal":647},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":454,"config":455},"Quelltext der Seite anzeigen",{"href":456,"dataGaName":457,"dataGaLocation":458},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":460,"config":461},"Diese Seite bearbeiten",{"href":462,"dataGaName":463,"dataGaLocation":458},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":465,"config":466},"Beteilige dich",{"href":467,"dataGaName":468,"dataGaLocation":458},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":470,"facebook":471,"youtube":472,"linkedin":473},"https://x.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[475,498,553,580,614],{"title":61,"links":476,"subMenu":481},[477],{"text":478,"config":479},"DevSecOps-Plattform",{"href":70,"dataGaName":480,"dataGaLocation":458},"devsecops platform",[482],{"title":184,"links":483},[484,488,493],{"text":485,"config":486},"Tarife anzeigen",{"href":186,"dataGaName":487,"dataGaLocation":458},"view plans",{"text":489,"config":490},"Vorteile von Premium",{"href":491,"dataGaName":492,"dataGaLocation":458},"/de-de/pricing/premium/","why premium",{"text":494,"config":495},"Vorteile von Ultimate",{"href":496,"dataGaName":497,"dataGaLocation":458},"/de-de/pricing/ultimate/","why ultimate",{"title":499,"links":500},"Lösungen",[501,506,509,511,516,521,525,528,531,536,538,540,543,548],{"text":502,"config":503},"Digitale Transformation",{"href":504,"dataGaName":505,"dataGaLocation":458},"/de-de/topics/digital-transformation/","digital transformation",{"text":507,"config":508},"Sicherheit und Compliance",{"href":126,"dataGaName":133,"dataGaLocation":458},{"text":118,"config":510},{"href":102,"dataGaName":103,"dataGaLocation":458},{"text":512,"config":513},"Agile Entwicklung",{"href":514,"dataGaName":515,"dataGaLocation":458},"/de-de/solutions/agile-delivery/","agile delivery",{"text":517,"config":518},"Cloud-Transformation",{"href":519,"dataGaName":520,"dataGaLocation":458},"/de-de/topics/cloud-native/","cloud transformation",{"text":522,"config":523},"SCM",{"href":115,"dataGaName":524,"dataGaLocation":458},"source code management",{"text":106,"config":526},{"href":108,"dataGaName":527,"dataGaLocation":458},"continuous integration & delivery",{"text":156,"config":529},{"href":158,"dataGaName":530,"dataGaLocation":458},"value stream management",{"text":532,"config":533},"GitOps",{"href":534,"dataGaName":535,"dataGaLocation":458},"/de-de/solutions/gitops/","gitops",{"text":169,"config":537},{"href":171,"dataGaName":172,"dataGaLocation":458},{"text":174,"config":539},{"href":176,"dataGaName":177,"dataGaLocation":458},{"text":541,"config":542},"Öffentlicher Sektor",{"href":181,"dataGaName":182,"dataGaLocation":458},{"text":544,"config":545},"Bildungswesen",{"href":546,"dataGaName":547,"dataGaLocation":458},"/de-de/solutions/education/","education",{"text":549,"config":550},"Finanzdienstleistungen",{"href":551,"dataGaName":552,"dataGaLocation":458},"/de-de/solutions/finance/","financial services",{"title":189,"links":554},[555,557,559,561,564,566,568,570,572,574,576,578],{"text":201,"config":556},{"href":203,"dataGaName":204,"dataGaLocation":458},{"text":206,"config":558},{"href":208,"dataGaName":209,"dataGaLocation":458},{"text":211,"config":560},{"href":213,"dataGaName":214,"dataGaLocation":458},{"text":216,"config":562},{"href":218,"dataGaName":563,"dataGaLocation":458},"docs",{"text":239,"config":565},{"href":241,"dataGaName":242,"dataGaLocation":458},{"text":234,"config":567},{"href":236,"dataGaName":237,"dataGaLocation":458},{"text":244,"config":569},{"href":246,"dataGaName":247,"dataGaLocation":458},{"text":252,"config":571},{"href":254,"dataGaName":255,"dataGaLocation":458},{"text":257,"config":573},{"href":259,"dataGaName":260,"dataGaLocation":458},{"text":262,"config":575},{"href":264,"dataGaName":265,"dataGaLocation":458},{"text":267,"config":577},{"href":269,"dataGaName":270,"dataGaLocation":458},{"text":272,"config":579},{"href":274,"dataGaName":275,"dataGaLocation":458},{"title":290,"links":581},[582,584,586,588,590,592,594,598,603,605,607,609],{"text":297,"config":583},{"href":299,"dataGaName":292,"dataGaLocation":458},{"text":302,"config":585},{"href":304,"dataGaName":305,"dataGaLocation":458},{"text":310,"config":587},{"href":312,"dataGaName":313,"dataGaLocation":458},{"text":315,"config":589},{"href":317,"dataGaName":318,"dataGaLocation":458},{"text":320,"config":591},{"href":322,"dataGaName":323,"dataGaLocation":458},{"text":325,"config":593},{"href":327,"dataGaName":328,"dataGaLocation":458},{"text":595,"config":596},"Sustainability",{"href":597,"dataGaName":595,"dataGaLocation":458},"/sustainability/",{"text":599,"config":600},"Vielfalt, Inklusion und Zugehörigkeit",{"href":601,"dataGaName":602,"dataGaLocation":458},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":330,"config":604},{"href":332,"dataGaName":333,"dataGaLocation":458},{"text":340,"config":606},{"href":342,"dataGaName":343,"dataGaLocation":458},{"text":345,"config":608},{"href":347,"dataGaName":348,"dataGaLocation":458},{"text":610,"config":611},"Transparenzerklärung zu moderner Sklaverei",{"href":612,"dataGaName":613,"dataGaLocation":458},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":615,"links":616},"Nimm Kontakt auf",[617,620,625,627,632,637,642],{"text":618,"config":619},"Sprich mit einem Experten/einer Expertin",{"href":52,"dataGaName":53,"dataGaLocation":458},{"text":621,"config":622},"Support",{"href":623,"dataGaName":624,"dataGaLocation":458},"https://support.gitlab.com/hc/en-us/articles/11626483177756-GitLab-Support","get help",{"text":364,"config":626},{"href":366,"dataGaName":367,"dataGaLocation":458},{"text":628,"config":629},"Status",{"href":630,"dataGaName":631,"dataGaLocation":458},"https://status.gitlab.com/","status",{"text":633,"config":634},"Nutzungsbedingungen",{"href":635,"dataGaName":636,"dataGaLocation":458},"/terms/","terms of use",{"text":638,"config":639},"Datenschutzerklärung",{"href":640,"dataGaName":641,"dataGaLocation":458},"/de-de/privacy/","privacy statement",{"text":643,"config":644},"Cookie-Einstellungen",{"dataGaName":645,"dataGaLocation":458,"id":646,"isOneTrustButton":29},"cookie preferences","ot-sdk-btn",{"items":648},[649,651,653],{"text":633,"config":650},{"href":635,"dataGaName":636,"dataGaLocation":458},{"text":638,"config":652},{"href":640,"dataGaName":641,"dataGaLocation":458},{"text":643,"config":654},{"dataGaName":645,"dataGaLocation":458,"id":646,"isOneTrustButton":29},[656],{"id":657,"title":18,"body":8,"config":658,"content":661,"description":8,"extension":27,"meta":664,"navigation":29,"path":665,"seo":666,"stem":667,"__hash__":668},"blogAuthors/en-us/blog/authors/radovan-bacovic.yml",{"template":659,"gitlabHandle":660},"BlogAuthor","rbacovic",{"name":18,"config":662},{"headshot":663},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1760036179/vvj2tiujit6kopuz8mqp.png",{},"/en-us/blog/authors/radovan-bacovic",{},"en-us/blog/authors/radovan-bacovic","7j6OYSuOOZuQmPOFNSQVXMc_aFk4xFi30am_VXHBytg",[670,683,695],{"content":671,"config":681},{"body":672,"title":673,"description":674,"authors":675,"heroImage":677,"date":678,"category":9,"tags":679},"## 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.",[676],"Omid Khan","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772721753/frfsm1qfscwrmsyzj1qn.png","2026-04-09",[106,680,26,24],"DevOps platform",{"featured":29,"template":12,"slug":682},"5-ways-gitlab-pipeline-logic-solves-real-engineering-problems",{"content":684,"config":693},{"title":685,"description":686,"authors":687,"heroImage":689,"date":690,"body":691,"category":9,"tags":692},"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.",[688],"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.",[26,23,24],{"featured":11,"template":12,"slug":694},"using-gitlab-container-virtual-registry-with-docker-hardened-images",{"content":696,"config":707},{"category":9,"tags":697,"body":699,"date":700,"heroImage":701,"authors":702,"title":705,"description":706},[26,698,106],"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",[703,704],"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":29,"template":12,"slug":708},"migration-from-azure-devops-to-gitlab",{"promotions":710},[711,725,737,748],{"id":712,"categories":713,"header":715,"text":716,"button":717,"image":722},"ai-modernization",[714],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":718,"config":719},"Get your AI maturity score",{"href":720,"dataGaName":721,"dataGaLocation":242},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":723},{"src":724},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":726,"categories":727,"header":729,"text":716,"button":730,"image":734},"devops-modernization",[23,728],"devsecops","Are you just managing tools or shipping innovation?",{"text":731,"config":732},"Get your DevOps maturity score",{"href":733,"dataGaName":721,"dataGaLocation":242},"/assessments/devops-modernization-assessment/",{"config":735},{"src":736},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":738,"categories":739,"header":740,"text":716,"button":741,"image":745},"security-modernization",[25],"Are you trading speed for security?",{"text":742,"config":743},"Get your security maturity score",{"href":744,"dataGaName":721,"dataGaLocation":242},"/assessments/security-modernization-assessment/",{"config":746},{"src":747},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":749,"paths":750,"header":752,"text":753,"button":754,"image":759},"github-azure-migration",[708,751],"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":755,"config":756},"See how GitLab compares to GitHub",{"href":757,"dataGaName":758,"dataGaLocation":242},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":760},{"src":736},{"header":762,"blurb":763,"button":764,"secondaryButton":769},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":765,"config":766},"Kostenlosen Test starten",{"href":767,"dataGaName":48,"dataGaLocation":768},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/de-de/","feature",{"text":50,"config":770},{"href":52,"dataGaName":53,"dataGaLocation":768},1776444457409]