Claude Code & Dagger: CI/CD-Pipelines als Code schreiben

Wer schon einmal um 23 Uhr einen GitHub-Actions-Workflow debuggt hat, weil ein Schritt auf dem CI-Runner scheitert, der lokal problemlos läuft, kennt das Grundproblem: YAML ist kein Code. Es gibt keine Typen, kein Refactoring, keine Tests, keine lokale Ausführung. Was bleibt, ist Push-and-Pray — pushen und auf Grün hoffen.

Dagger löst dieses Problem von der Wurzel: Pipelines werden in echtem Code geschrieben — Python, TypeScript oder Go — und laufen in Containern, die lokal identisch zum CI-Runner verhalten. Claude Code beschleunigt dabei genau die Stellen, die sonst am meisten Zeit fressen: Pipeline-Struktur aufsetzen, Caching konfigurieren, Secrets sicher übergeben, Fehler diagnostizieren.

Claude Code Mastery — CI/CD, Agents und Workflows auf Deutsch

Lerne, wie du Claude Code produktiv in deinen gesamten Entwicklungsablauf integrierst — von der Pipeline bis zum autonomen Agenten. Einmalig bezahlt, kein Abo.

Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

1. Dagger vs. traditionelles CI: Was ist der Unterschied?

GitHub Actions, Jenkins, CircleCI — alle funktionieren nach demselben Prinzip: du schreibst eine YAML-Konfiguration, die beschreibt, was auf dem Server passieren soll. Das System liest die YAML, erzeugt einen Runner, führt Schritte aus. Das klingt einfach, hat aber drei strukturelle Nachteile.

Erstens: keine lokale Ausführung. YAML-Pipelines laufen immer remote. Jede Änderung bedeutet einen Push, ein Warten auf den Runner, ein Lesen der Logs. Der Feedback-Loop ist langsam — oft 3 bis 10 Minuten pro Iteration.

Zweitens: YAML ist kein Programmierkontext. Es gibt keine Variablen im eigentlichen Sinne, keine Funktionen, keine Typen, kein Refactoring. Wiederverwendung geschieht über Actions oder Shared Workflows — beides hat eigene Grenzen und Lernkurven.

Drittens: Portabilität ist nur auf dem Papier. Ein GitHub-Actions-Workflow läuft nicht in Jenkins. Ein Jenkins-Pipeline läuft nicht in GitLab CI. Wechselt die CI-Plattform, wird alles neu geschrieben.

Dagger löst alle drei Probleme: Pipelines laufen lokal, sind echter Code in der Sprache deiner Wahl, und funktionieren auf jedem CI-System weil sie sich selbst als Container ausführen. Der CI-Runner ist nur noch ein Startpunkt — die eigentliche Logik liegt in deinem Code.

Dagger in einem Satz: Du schreibst eine Funktion in Python, TypeScript oder Go, die einen Dagger-Client benutzt, um Container zu starten, Code darin auszuführen und Ergebnisse zurückzugeben. Diese Funktion kannst du lokal aufrufen oder von jedem CI-System aus starten.

2. Erste Pipeline in Python — mit Claude Code

Das schnellste Onboarding: Claude Code beschreibt, was die Pipeline tun soll, und bekommt direkt den lauffähigen Code zurück.

claude "Schreib mir eine Dagger-Pipeline in Python, die:
1. Den Code aus dem aktuellen Verzeichnis mounted
2. Mit Python 3.12 die Dependencies installiert
3. pytest ausführt
4. Das Ergebnis als Exit-Code zurückgibt"

Claude Code liest dein Projekt — pyproject.toml, requirements.txt, vorhandene Testdateien — und schreibt eine pipeline.py, die genau zu deiner Struktur passt. Kein generisches Template, sondern Code, der die tatsächlichen Abhängigkeiten und Testpfade kennt.

Das Ergebnis sieht typischerweise so aus:

import dagger
import anyio

async def main():
    async with dagger.Connection() as client:
        src = client.host().directory(".", exclude=[".git", "__pycache__"])

        result = await (
            client.container()
            .from_("python:3.12-slim")
            .with_mounted_directory("/src", src)
            .with_workdir("/src")
            .with_exec(["pip", "install", "-r", "requirements.txt"])
            .with_exec(["pytest", "tests/", "-v"])
            .stdout()
        )
        print(result)

anyio.run(main)

Diese Pipeline läuft lokal mit python pipeline.py — und identisch auf GitHub Actions, GitLab CI oder Jenkins, weil Dagger den Container selbst managed.

3. Caching richtig nutzen

Einer der größten Geschwindigkeitsgewinne mit Dagger ist Layer-Caching, das über Pipeline-Runs hinweg erhalten bleibt. In YAML-Pipelines ist Caching eine separate Konfiguration mit eigenen Fallstricken. In Dagger ist es ein expliziter API-Aufruf im Code.

claude "Meine Dagger-Pipeline installiert bei jedem Run alle
npm-Packages neu. Füge Caching für node_modules hinzu, das zwischen
Runs erhalten bleibt."

Claude Code ergänzt den Code um einen cache_volume-Mount — Dagger verwaltet diesen Cache automatisch:

node_cache = client.cache_volume("node-modules-cache")

container = (
    client.container()
    .from_("node:20-alpine")
    .with_mounted_directory("/app", src)
    .with_mounted_cache("/app/node_modules", node_cache)
    .with_workdir("/app")
    .with_exec(["npm", "ci"])
    .with_exec(["npm", "run", "build"])
)

Das Ergebnis: der erste Run installiert alle Packages. Alle folgenden Runs überspringen npm ci vollständig, wenn sich package-lock.json nicht geändert hat. Lokal wie auf dem CI-Runner.

4. Secrets sicher übergeben

Secrets gehören nicht in den Code. Nicht als Kommentar, nicht als Fallback-String, nicht als hartcodierter Default. Dagger hat eine eigene Secret-API, die sicherstellt, dass Werte nie in Logs oder Container-Layern landen.

Grundregel: Secrets kommen immer aus Umgebungsvariablen. Niemals hardcoded im Quellcode, niemals in YAML-Dateien im Repository, niemals in Kommentaren. Dagger’s set_secret-API stellt sicher, dass der Wert in keinem Log erscheint.

claude "Meine Pipeline muss einen Docker-Hub-Login durchführen.
Zeig mir, wie ich das Passwort als Dagger-Secret übergebe,
ohne es in Logs sichtbar zu machen."

Claude Code generiert den korrekten Pattern — Secret aus der Umgebung lesen, als Dagger-Secret registrieren, als Mount übergeben:

import os

# Secret kommt aus der Umgebungsvariable — niemals hardcoden
registry_password = client.set_secret(
    "registry_password",
    os.environ["REGISTRY_PASSWORD"]
)

container = (
    client.container()
    .with_registry_auth(
        "docker.io",
        username=os.environ["REGISTRY_USER"],
        secret=registry_password
    )
    .from_("myapp:latest")
)

Der entscheidende Unterschied zu YAML: Das Secret taucht in keinem Log auf, weil Dagger es intern als versiegelten Wert behandelt. Die Umgebungsvariable wird nur einmal eingelesen — danach arbeitet die Pipeline mit dem Secret-Handle, nicht mit dem Klartextwert.

5. Lokal testen vor dem Push

Der größte praktische Vorteil von Dagger: Du testest lokal, bevor du pushst. Kein Commit-Raten, kein 5-Minuten-Warten auf den CI-Runner nur um zu sehen, dass ein Schritt nicht gefunden wird.

claude "Meine Pipeline schlägt auf CI mit 'command not found: poetry' fehl,
lokal funktioniert alles. Was könnte der Unterschied sein und wie
debugge ich das in Dagger?"

Claude Code analysiert die Pipeline, findet den Grund — meistens ein Base-Image, das das Tool nicht enthält — und zeigt, wie man einen interaktiven Shell-Session im Container öffnet um das Problem zu reproduzieren:

# Interaktiver Debug-Modus: Shell im Pipeline-Container öffnen
dagger call debug-shell

Oder direkt im Python-Code einen Breakpoint einbauen:

# Container anhalten und Shell starten statt Pipeline weiterzuführen
terminal = (
    container
    .with_exec(["sh", "-c", "which poetry || echo 'not found'"])
    .terminal()
)
await terminal

Das ist ein Paradigmenwechsel: Statt Logs auf dem CI-Runner zu lesen, öffnest du eine Shell im exakt gleichen Container, den die Pipeline benutzt — lokal, sofort, ohne Push.

6. Dagger in TypeScript und Go

Dagger ist nicht Python-exklusiv. Für TypeScript-Projekte schreibt Claude Code die Pipeline in TypeScript, für Go-Projekte in Go. Die API ist konsistent — nur die Syntax ändert sich.

claude "Ich habe eine Node.js-App. Schreib mir eine Dagger-Pipeline
in TypeScript, die baut, testet und ein Docker-Image erstellt."

Claude Code generiert src/index.ts mit dem Dagger TypeScript SDK — inklusive korrekter Typen, Fehlerbehandlung und einer dagger.json-Konfiguration. Der Vorteil: TypeScript-Entwickler müssen keine neue Sprache lernen, um ihre Pipeline zu verstehen und zu erweitern.

Multi-Pipeline-Struktur mit Claude Code

Bei größeren Projekten empfiehlt sich eine Modularisierung: eine Funktion pro Pipeline-Schritt, eine zentrale Orchestrierung. Claude Code hilft dabei, die Struktur sauber zu halten:

claude "Refaktoriere meine Dagger-Pipeline so, dass Build, Test
und Deploy als separate Funktionen definiert sind, die ich
unabhängig oder in Sequenz aufrufen kann."

7. Claude Code-Tipps für Dagger-Projekte

Aus dem praktischen Einsatz haben sich einige Patterns bewährt, die Claude Code besonders gut unterstützt:

"Mit Dagger und Claude Code habe ich eine 8-minütige GitHub-Actions-Pipeline auf 90 Sekunden gebracht — durch Layer-Caching, das YAML mir nie so klar gemacht hätte."

8. Integration in bestehende CI-Systeme

Dagger ersetzt nicht deinen CI-Runner — es läuft auf ihm. Die Integration in GitHub Actions reduziert sich auf wenige Zeilen:

jobs:
  pipeline:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Dagger Pipeline
        env:
          REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
        run: python pipeline.py

Das ist der gesamte Actions-Workflow. Die eigentliche Logik — was gebaut, getestet und deployed wird — steht in pipeline.py, nicht in der YAML. Wechselst du zu GitLab CI, änderst du diese zehn Zeilen. Die Pipeline selbst bleibt unverändert.

Verwandte Artikel die gut dazu passen:


Claude Code Mastery — von der Pipeline bis zum produktiven Agenten

CI/CD ist eine Stärke von Claude Code — aber nicht die einzige. Lerne Agents, MCP-Server, Hooks und Multi-Agent-Workflows. Vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

Kurs · Claude Code Mastery

Von der Pipeline zum produktiven AI-Agenten

CI/CD. Dagger. Agents. MCP. Hooks. Multi-Agent-Workflows. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht