Containerhafen bei Nacht mit Kränen und Lichtspiegelungen im Wasser
Zurück zum Blog
dotnetaspiredeployment

.NET Aspire 9 und das Deployment-Problem

Sven HennessenDevOps

Aspire 9 versprach orchestrierte Microservices – doch beim Deployment offenbarte sich ein kritischer blinder Fleck: Helm-Charts ließen sich zwar generieren, deployen aber nicht.

Was Aspire 9 gut machte

.NET Aspire 9 löste ein echtes Problem: Die lokale Orchestrierung verteilter .NET-Anwendungen war bisher mühsam. Wer mehrere Services, eine Datenbank, einen Message Broker und ein Frontend gleichzeitig starten und miteinander verdrahten wollte, jonglierte mit Skripten, docker-compose-Dateien und manuell gesetzten Umgebungsvariablen.

Aspire 9 brachte dafür den AppHost – ein C#-Projekt, das die gesamte Anwendungsstruktur als Code beschreibt:

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("db");
var api = builder.AddProject<Projects.Api>("api")
    .WithReference(postgres);
var frontend = builder.AddProject<Projects.Frontend>("frontend")
    .WithReference(api);

builder.Build().Run();

Lokale Entwicklung, Service Discovery, das integrierte Dashboard für Logs und Traces – das alles funktionierte gut. Der AppHost wusste, welche Services voneinander abhängig waren, setzte Verbindungsstrings automatisch und startete alles in der richtigen Reihenfolge.

Das Problem begann genau dann, wenn man diese lokal funktionierende Anwendung tatsächlich irgendwo deployen wollte.

Das Deployment-Problem in Aspire 9

Der primäre Weg: azd und Azure Container Apps

Das offizielle und vollständig unterstützte Deployment-Modell in Aspire 9 führte durch den Azure Developer CLI (azd). Man generierte ein Manifest – eine JSON-Datei, die alle Ressourcen und ihre Verbindungen als statische Momentaufnahme beschrieb – und übergab es an azd, das daraus Azure Bicep Templates erzeugte und alles als Azure Container Apps provisionierte.

azd up

Für Azure war das ein funktionierender Weg. Wer AWS, GCP oder eigene Infrastruktur betrieb, fand in der offiziellen Dokumentation nichts Vergleichbares. Das Manifest war theoretisch auch von anderen Tools lesbar, aber das war Eigeninitiative – kein unterstützter Workflow.

Das Manifest-Modell und seine strukturellen Grenzen

Das Manifest war eine statische Momentaufnahme des AppHost-Modells zum Zeitpunkt der Generierung. Komplexe Abhängigkeiten, bedingte Konfigurationen oder Ressourcen, die erst zur Laufzeit konkrete Werte erhalten – all das ließ sich in einem statischen JSON-Dokument nicht vollständig ausdrücken. Was lokal im AppHost funktionierte, musste beim Export manchmal vereinfacht oder weggelassen werden.

Hinzu kam: Jedes azd up begann bei null. Das Tool erinnerte sich nicht an die zuletzt verwendete Subscription, Region oder Ressourcengruppe. Wer regelmäßig deployte, beantwortete dieselben Fragen bei jedem Durchlauf erneut.

Kubernetes: Helm-Charts ja, deployen nein

Ab Aspire 9.2 (April 2025) gab es mit Aspire.Hosting.Kubernetes tatsächlich einen offiziellen Kubernetes Publisher – allerdings als Preview, und mit einer entscheidenden Einschränkung: Er konnte Helm-Charts generieren, aber nicht deployen.

Der Publisher erzeugte aus dem AppHost eine vollständige Helm-Chart-Struktur:

Chart.yaml          ← Metadaten
values.yaml         ← Konfigurationsparameter
templates/
  deployment.yaml   ← Pro Service
  service.yaml
  configmap.yaml
  secret.yaml

Der nächste Schritt – helm install oder helm upgrade gegen ein echtes Cluster – lag vollständig beim Entwickler. aspire deploy existierte für Kubernetes in der gesamten 9.x-Reihe nicht. Es gab also eine Lücke genau in der Mitte des Workflows.

Dazu kamen konkrete Bugs, die den Einsatz in der Praxis erschwerten. Port-Felder wurden als Strings statt als int32 serialisiert, was helm upgrade zum Fehlschlagen brachte. Init-Container – etwa für Datenbankmigrationen – wurden nicht korrekt in die generierten Manifeste übernommen. Die zentrale values.yaml erforderte manuelle --set-Overrides beim Deployment, weil Konfiguration nicht direkt in ConfigMaps oder Secrets eingebettet wurde.

Auch Docker Compose war ab 9.2 als Publisher verfügbar, aber ebenfalls nur als Preview und ohne aspire deploy-Unterstützung.

Die Community als Lückenbüßer: aspirate

Weil der offizielle Weg für Kubernetes unvollständig blieb, entstand in der Community das Tool aspirate (auch bekannt als Aspir8). Es las das Aspire-Manifest und übernahm was der offizielle Publisher ausließ: Registry-Push, Secret-Management, und direktes Cluster-Deployment via aspirate apply.

aspirate funktionierte, war aber ein Workaround. Updates liefen der Aspire-Entwicklung hinterher, und neue Aspire-Funktionen brauchten Zeit, bis sie im Community-Tool ankamen.

Zusammenfassung: Was in Aspire 9 fehlte

  • Azure Container Apps war das einzige vollständig unterstützte Deployment-Target
  • Kubernetes: Helm-Chart-Generierung möglich (Preview ab 9.2), aber kein aspire deploy – manuelles helm install erforderlich
  • Docker Compose: Publisher vorhanden (Preview ab 9.2), aber kein vollständiger Deploy-Pfad
  • Azure App Service: erst ab 9.3 als Preview, nicht stabil
  • Kein AWS, kein GCP, kein On-Premises – weder offiziell noch als erweiterbar konzipiert
  • Statisches Manifest-Modell ohne Zustandsgedächtnis und mit strukturellen Ausdrucksgrenzen
  • Kein Erweiterungsmodell für eigene Deployment-Targets

Wie Aspire 13 das änderte

13.0 – Das Fundament: Pipeline-Modell und State Persistence (November 2025)

Der größte Einschnitt war architektonischer Natur. Das Manifest-basierte Publisher-Modell wurde vollständig ersetzt durch ein Pipeline-Modell: Jede Ressource im AppHost trägt eigene PipelineStep-Objekte bei, die Abhängigkeiten untereinander deklarieren, parallel ausgeführt werden wo möglich, und als einzelne Einheiten beobachtbar sind. Die alten APIs (IDistributedApplicationPublisher, WithPublishingCallback, PublishingContext) wurden entfernt.

aspire deploy   # oder: aspire do deploy

Zum ersten Mal merkte sich Aspire den Zustand zwischen Deployments: Subscription, Ressourcengruppe, Region und Parameterwerte werden pro Entwickler und Umgebung gespeichert und beim nächsten Durchlauf vorbelegt.

Stabil unterstützt waren in 13.0 Azure Container Apps und Azure App Service. Docker Compose und Kubernetes waren vorhanden, aber noch in Arbeit.

13.1 – Container Registry und Docker Compose Deploy (Dezember 2025)

Container Registries wurden in 13.1 zu einem eigenständigen Konzept im AppHost:

var registry = builder.AddContainerRegistry("myregistry", "registry.example.com");
var api = builder.AddProject<Projects.Api>("api")
    .WithContainerRegistry(registry);

Statt implizit an ein Azure Container Registry gebunden zu sein, konnte die Registry nun separat provisioniert, wiederverwendet oder auf externe Registries (Docker Hub, GHCR) gezeigt werden. Die Deployment-Pipeline enthielt damit einen expliziten push-Schritt, der unabhängig von anderen Schritten parallel laufen konnte.

Docker Compose erhielt in 13.1 erstmals vollständige Deploy-Unterstützungaspire deploy funktionierte damit end-to-end, ohne manuellen Docker-CLI-Eingriff.

Für Azure App Service kam Deployment Slots dazu: Zero-Downtime-Deployments via Staging-Slot und automatischem Swap waren direkt aus dem AppHost konfigurierbar.

13.2 – Docker Compose erreicht Stable, Azure Netzwerk-Isolation (März 2026)

Docker Compose verließ in 13.2 den Preview-Status und wurde stable. Damit gab es erstmals einen vollständig unterstützten, nicht-Azure-Deployment-Pfad.

Neu hinzu kamen Infrastruktur-as-Code-APIs für Netzwerkisolation in Azure-Deployments:

builder.AddAzureVirtualNetwork("vnet")
    .AddSubnet("app-subnet")
    .AddPrivateEndpoint(postgres);

Azure Virtual Networks, Private Endpoints mit automatischer DNS-Zonenkonfiguration und Network Security Groups ließen sich direkt im AppHost deklarieren. Für Kubernetes gab es außerdem Bugfixes in der YAML-Serialisierung, die in 9.x zu helm upgrade-Fehlern geführt hatten.

13.3 – End-to-End Kubernetes, AKS und aspire destroy (Mai 2026)

Der entscheidende Schritt für Kubernetes: aspire deploy funktionierte jetzt vollständig gegen echte Cluster. Der Workflow lief durch helm install bzw. helm upgrade unter der Haube – ohne manuellen Eingriff. aspire destroy führte entsprechend helm uninstall aus und entfernte Namespaces.

Für Azure Kubernetes Service (AKS) kam ein eigenes Hosting-Paket dazu (Aspire.Hosting.Azure.Kubernetes):

builder.AddAzureKubernetesEnvironment("prod-aks")
    .WithHelm();

Aspire generierte daraus eine kombinierte Bicep- und Helm-Pipeline: Cluster-Provisionierung, Image-Push und Helm-Deployment in einem Durchlauf. Ingress und Gateway API wurden als erste Klasse Konzepte eingeführt – Routing-Regeln, TLS-Terminierung via cert-manager und FQDN-Zuweisung ließen sich direkt im AppHost ausdrücken, ohne separate Kubernetes-YAML-Dateien.

13.4 – Generelle Verfügbarkeit und externe Helm-Charts (Juni 2026)

Mit 13.4 verließen aspire publish, aspire deploy und aspire destroy den Preview-Status. Die Kubernetes-Unterstützung erhielt cert-manager mit Let's Encrypt-Integration als typisierte API sowie Azure Application Gateway for Containers (AGC) für AKS-Deployments.

Neu: AddHelmChart ermöglicht die Installation beliebiger externer Helm-Charts als Teil der Deployment-Pipeline:

builder.AddHelmChart("nginx-ingress", "ingress-nginx/ingress-nginx", "4.10.0");

Damit können eigene Aspire-Ressourcen neben Standard-Ecosystem-Charts (Ingress Controller, Monitoring Stacks) deployed werden, ohne die Pipeline zu verlassen.

Weiterhin nicht offiziell unterstützt sind AWS, GCP und On-Premises-Deployments. Das Pipeline-Modell ist durch WithPipelineStepFactory erweiterbar, aber eigene Provider müssen vollständig selbst gebaut werden.

Unterstützung benötigt?

Du evaluierst Aspire für euer Projekt oder stehst vor der Migration von Aspire 9 auf 13, aber bist unsicher, welcher Deployment-Pfad zu eurer Infrastruktur passt? Wir helfen dir gerne dabei! Melde dich einfach über unsere Kontaktseite und wir schauen gemeinsam, wie wir eure Aspire-Strategie optimal aufsetzen können.