Skip to main content
.NET Aspire applications can be deployed to any Kubernetes cluster, including Azure Kubernetes Service (AKS), Amazon EKS, Google GKE, and on-premises clusters.

Prerequisites

  • Kubernetes cluster (1.19+)
  • kubectl installed and configured
  • Helm 3.x installed
  • Container registry accessible from your cluster
  • .NET Aspire application with AppHost project
  • Aspire CLI installed (dotnet tool install -g aspire)

Kubernetes Deployment Model

Aspire generates Kubernetes manifests from your application model, creating:
  • Deployments - For container workloads
  • Services - For service discovery and load balancing
  • ConfigMaps - For configuration data
  • Secrets - For sensitive configuration
  • Ingress - For external HTTP/HTTPS access (optional)

Setting Up Kubernetes Environment

Add a Kubernetes environment to your AppHost:
var builder = DistributedApplication.CreateBuilder(args);

// Add Kubernetes environment
var k8s = builder.AddKubernetesEnvironment("k8s");

// Add your services
var cache = builder.AddRedis("cache");

var apiService = builder.AddProject<Projects.ApiService>("apiservice")
    .WithReference(cache);

builder.Build().Run();
The Kubernetes environment resource is implemented in src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs and automatically generates Helm charts from your app model.

Generating Kubernetes Manifests

Use the Aspire CLI to generate deployment artifacts:
# Generate Kubernetes manifests
aspire publish --output-path ./k8s-artifacts
This creates:
k8s-artifacts/
├── aspire-manifest.json          # Aspire manifest
├── Chart.yaml                     # Helm chart metadata
├── values.yaml                    # Default values
└── templates/
    ├── apiservice-deployment.yaml # Deployment for API service
    ├── apiservice-service.yaml    # Service for API
    ├── cache-deployment.yaml      # Redis deployment
    └── cache-service.yaml         # Redis service

Helm Chart Generation

The Kubernetes environment automatically generates a Helm chart with the application name:
var k8s = builder.AddKubernetesEnvironment("k8s");

// The Helm chart name is derived from the application name
// Located in: src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentExtensions.cs:40
resource.HelmChartName = builder.Environment.ApplicationName.ToHelmChartName();

Deploying to Kubernetes

Using Helm

# Install the Helm chart
helm install myapp ./k8s-artifacts

# Upgrade an existing deployment
helm upgrade myapp ./k8s-artifacts

# Uninstall
helm uninstall myapp

Using kubectl

# Apply all manifests
kubectl apply -f ./k8s-artifacts/templates/

# Check deployment status
kubectl get deployments
kubectl get pods
kubectl get services

# Delete resources
kubectl delete -f ./k8s-artifacts/templates/

Customizing Kubernetes Resources

Configuring Deployments

Customize Kubernetes deployments for your resources:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithReplicas(3)  // Set replica count
    .WithEnvironment("ASPNETCORE_ENVIRONMENT", "Production");

Service Configuration

Configure Kubernetes services:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithHttpEndpoint(port: 8080, name: "http")
    .WithHttpsEndpoint(port: 8443, name: "https");
This generates a Kubernetes Service:
apiVersion: v1
kind: Service
metadata:
  name: apiservice
spec:
  selector:
    app: apiservice
  ports:
  - name: http
    port: 8080
    targetPort: 8080
    protocol: TCP
  - name: https
    port: 8443
    targetPort: 8443
    protocol: TCP
  type: ClusterIP

External Endpoints

Expose services externally:
builder.AddProject<Projects.WebApp>("webapp")
    .WithExternalHttpEndpoints();
This creates an Ingress resource for external access.

Container Images

Building and Pushing Images

Before deploying, build and push container images:
# For .NET projects, Aspire generates Dockerfiles
# Build and push manually or use CI/CD

docker build -t myregistry.azurecr.io/apiservice:latest ./ApiService
docker push myregistry.azurecr.io/apiservice:latest

Using Private Registries

Configure Kubernetes to use a private registry:
# Create a docker-registry secret
kubectl create secret docker-registry regcred \
  --docker-server=myregistry.azurecr.io \
  --docker-username=<username> \
  --docker-password=<password>
Reference the secret in your deployment:
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: apiservice
    image: myregistry.azurecr.io/apiservice:latest

Configuration Management

ConfigMaps

Aspire generates ConfigMaps for non-sensitive configuration:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithEnvironment("LOG_LEVEL", "Information")
    .WithEnvironment("FEATURE_FLAGS", "EnableNewUI=true");
Generated ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
  name: apiservice-config
data:
  LOG_LEVEL: "Information"
  FEATURE_FLAGS: "EnableNewUI=true"

Secrets

Use Kubernetes Secrets for sensitive data:
var password = builder.AddParameter("db-password", secret: true);

var db = builder.AddPostgres("postgres")
    .WithEnvironment("POSTGRES_PASSWORD", password);

builder.AddProject<Projects.ApiService>("apiservice")
    .WithReference(db);
Generated Secret:
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
type: Opaque
data:
  POSTGRES_PASSWORD: <base64-encoded-password>

Resource Scaling

Horizontal Pod Autoscaling

Configure HPA for automatic scaling:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: apiservice-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: apiservice
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Resource Limits

Set CPU and memory limits:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithAnnotation(new ResourceLimitsAnnotation
    {
        CpuLimit = "1000m",
        MemoryLimit = "512Mi",
        CpuRequest = "100m",
        MemoryRequest = "128Mi"
    });

Service Discovery

Kubernetes DNS provides automatic service discovery:
// Reference another service
var cache = builder.AddRedis("cache");

builder.AddProject<Projects.ApiService>("apiservice")
    .WithReference(cache);  // Resolves to: cache:6379
In the container, the connection string resolves to the Kubernetes service DNS name.

Persistent Storage

Volumes for Databases

var postgres = builder.AddPostgres("postgres")
    .WithDataVolume();  // Creates a PersistentVolumeClaim

builder.AddProject<Projects.DataService>("dataservice")
    .WithReference(postgres);
Generated PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Bind Mounts

builder.AddContainer("nginx", "nginx")
    .WithBindMount("./static", "/usr/share/nginx/html", isReadOnly: true);

Health Checks

Aspire configures Kubernetes health probes:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apiservice
spec:
  template:
    spec:
      containers:
      - name: apiservice
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

Deploying to Cloud Kubernetes Services

Azure Kubernetes Service (AKS)

# Create AKS cluster
az aks create --resource-group myResourceGroup \
  --name myAKSCluster \
  --node-count 3 \
  --enable-addons monitoring

# Get credentials
az aks get-credentials --resource-group myResourceGroup --name myAKSCluster

# Deploy
helm install myapp ./k8s-artifacts

Amazon EKS

# Create EKS cluster
eksctl create cluster --name myCluster --region us-west-2

# Update kubeconfig
aws eks update-kubeconfig --region us-west-2 --name myCluster

# Deploy
helm install myapp ./k8s-artifacts

Google GKE

# Create GKE cluster
gcloud container clusters create myCluster --num-nodes=3

# Get credentials
gcloud container clusters get-credentials myCluster

# Deploy
helm install myapp ./k8s-artifacts

Observability

Logging

Aspire applications emit logs to stdout/stderr, which Kubernetes captures:
# View logs
kubectl logs deployment/apiservice

# Follow logs
kubectl logs -f deployment/apiservice

# Logs from all pods
kubectl logs -l app=apiservice

Metrics

Integrate with Prometheus for metrics:
apiVersion: v1
kind: Service
metadata:
  name: apiservice
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    prometheus.io/path: "/metrics"

Distributed Tracing

Configure OpenTelemetry export to a trace collector:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithEnvironment("OTEL_EXPORTER_OTLP_ENDPOINT", "http://jaeger:4317");

Best Practices

Organize resources by environment:
kubectl create namespace production
helm install myapp ./k8s-artifacts --namespace production
Always set resource requests and limits:
resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "512Mi"
    cpu: "1000m"
Ensure Kubernetes can manage your application lifecycle:
builder.AddProject<Projects.ApiService>("apiservice")
    .WithHealthCheck("/health");
Use external secret managers like Azure Key Vault or AWS Secrets Manager:
# Use External Secrets Operator
kubectl apply -f external-secret.yaml
Always tag images with versions, not latest:
docker build -t myregistry.azurecr.io/apiservice:v1.2.3 .

Troubleshooting

Check pod status and events:
kubectl describe pod <pod-name>
kubectl get events --sort-by='.lastTimestamp'

Next Steps

Deploy to Azure

Learn about deploying to Azure Container Apps

App Model

Understand the Aspire application model

Service Discovery

Learn how service discovery works

Hosting Resources

Explore available hosting integrations

Build docs developers (and LLMs) love