Deploy to Kubernetes clusters
Deploy your Aspire application to any existing Kubernetes cluster. Aspire generates a complete Helm chart from your application model, and you can either apply it yourself or let aspire deploy install it directly using your current kubectl context.
For hosting integration setup and configuration, see Kubernetes integration.
Prerequisites
Section titled “Prerequisites”- Aspire prerequisites
- Aspire CLI installed
- kubectl installed with a configured cluster context
- Helm installed and available on your
PATH - An existing Kubernetes cluster (local or remote)
Configure your AppHost
Section titled “Configure your AppHost”Add the Kubernetes hosting integration to your AppHost and configure a Kubernetes environment:
aspire add kubernetesThe Aspire CLI adds the 📦 Aspire.Hosting.Kubernetes integration to your AppHost.
Then add the Kubernetes environment in your AppHost:
var builder = DistributedApplication.CreateBuilder(args);
var k8s = builder.AddKubernetesEnvironment("k8s");
var api = builder.AddProject<Projects.MyApi>("api");
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const k8s = await builder.addKubernetesEnvironment('k8s');
const api = await builder .addNodeApp('api', './api', 'src/index.ts') .withHttpEndpoint({ env: 'PORT' }) .withExternalHttpEndpoints();
await builder.build().run();When a Kubernetes environment is present, all compute resources are automatically published as Kubernetes workloads — no additional opt-in is required.
Configure a container registry
Section titled “Configure a container registry”Unlike managed targets such as AKS, a vanilla Kubernetes cluster doesn’t know which container registry to use for your application images. Without a registry, Aspire has no way to make your locally-built images available to cluster nodes. You must provide a container registry that is accessible from both your local machine (to push images) and from the cluster (to pull images):
var builder = DistributedApplication.CreateBuilder(args);
var registry = builder.AddContainerRegistry("registry", "myregistry.example.com:5000");builder.AddKubernetesEnvironment("k8s").WithContainerRegistry(registry);
var api = builder.AddProject<Projects.MyApi>("api");
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const registry = await builder.addContainerRegistry('registry', 'myregistry.example.com:5000');const k8s = await builder.addKubernetesEnvironment('k8s');await k8s.withContainerRegistry(registry);
const api = await builder .addNodeApp('api', './api', 'src/index.ts') .withHttpEndpoint({ env: 'PORT' }) .withExternalHttpEndpoints();
await builder.build().run();The registry endpoint must be reachable from both your local machine and from the Kubernetes cluster nodes. When you run aspire deploy, Aspire builds your container images, pushes them to this registry, and configures the Helm chart to pull from it.
Publish Helm chart artifacts
Section titled “Publish Helm chart artifacts”To generate Kubernetes deployment artifacts without deploying, use aspire publish:
aspire publish -o k8s-artifactsThis command analyzes your application model and produces a complete Helm chart in the specified output directory:
Directoryk8s-artifacts/
- Chart.yaml Chart metadata (name, version, etc.)
- values.yaml Configurable values for the chart
Directorytemplates/
Directory<resource>/
- deployment.yaml Deployment or StatefulSet
- service.yaml Service for connectivity
- config.yaml Configuration data (ConfigMap)
The publisher generates the following Kubernetes resources from your application model:
- Deployments or StatefulSets for your application services
- Services for network connectivity between resources
- ConfigMaps for application configuration, connection strings, and environment variables
You can then deploy these artifacts using helm, kubectl, or your existing GitOps workflow.
Deploy to a cluster
Section titled “Deploy to a cluster”To deploy directly to your current Kubernetes cluster, use aspire deploy:
aspire deployaspire deploy uses your current kubectl context and deploys the application using helm install. Aspire builds container images, generates the Helm chart, and installs it to the cluster in a single command.
For subsequent deployments, aspire deploy automatically upgrades the existing Helm release.
Deploy published artifacts with Helm
Section titled “Deploy published artifacts with Helm”If you prefer to publish first and deploy separately — for example, in a CI/CD pipeline — use Helm to install the generated chart:
-
Install the chart to your cluster:
Install with Helm helm install my-app ./k8s-artifacts -
For subsequent deployments, upgrade the existing release:
Upgrade with Helm helm upgrade my-app ./k8s-artifacts -
Override default values using
--setflags or a custom values file:Override values helm upgrade my-app ./k8s-artifacts \--set api.image.repository=myregistry.azurecr.io/api \--set api.image.tag=v1.2.0Alternatively, create a custom values file for your environment:
values.production.yaml api:image:repository: myregistry.azurecr.io/apitag: v1.2.0Deploy with custom values file helm upgrade my-app ./k8s-artifacts -f values.production.yaml
Customize the Kubernetes environment
Section titled “Customize the Kubernetes environment”Helm settings
Section titled “Helm settings”Use WithHelm to configure Helm chart settings such as namespace, release name, and chart version:
builder.AddKubernetesEnvironment("k8s") .WithHelm(helm => { helm.WithNamespace("my-namespace"); helm.WithReleaseName("my-release"); helm.WithChartVersion("1.0.0"); });const k8s = await builder.addKubernetesEnvironment('k8s');await k8s.withHelm(async (helm) => { await helm.withNamespace('my-namespace'); await helm.withReleaseName('my-release'); await helm.withChartVersion('1.0.0');});Helm is the default deployment engine. Call WithHelm only when you need to customize the defaults.
Node pools
Section titled “Node pools”Use AddNodePool to reference an existing node pool in your cluster, then WithNodePool to schedule specific workloads on it:
var k8s = builder.AddKubernetesEnvironment("k8s");var gpuPool = k8s.AddNodePool("gpu");
builder.AddContainer("ml-worker", "my-ml-image") .WithNodePool(gpuPool);const k8s = await builder.addKubernetesEnvironment('k8s');const gpuPool = await k8s.addNodePool('gpu');
const worker = await builder.addContainer('ml-worker', 'my-ml-image');await worker.withNodePool(gpuPool);Per-resource customization
Section titled “Per-resource customization”Use PublishAsKubernetesService to modify the generated Kubernetes resources for individual services:
using Aspire.Hosting.Kubernetes.Resources;
builder.AddContainer("service", "nginx") .WithEnvironment("MY_ENV", "value") .PublishAsKubernetesService(resource => { // Scale to 3 replicas if (resource.Workload is Deployment deployment) { deployment.Spec.Replicas = 3; } });const service = await builder.addContainer('service', 'nginx');await service.withEnvironment('MY_ENV', 'value');await service.publishAsKubernetesService(async (resource) => { // Scale to 3 replicas await resource.workload.spec.replicas.set(3);});Deployment considerations
Section titled “Deployment considerations”Container images
Section titled “Container images”The publisher generates parameterized Helm values for container image references. If you haven’t specified custom container images, the generated values.yaml contains placeholders that you override at deployment time using --set or a custom values file.
Resource names
Section titled “Resource names”Resource names in Kubernetes must follow DNS naming conventions. The integration automatically normalizes Aspire resource names by:
- Converting to lowercase
- Replacing invalid characters with hyphens
- Ensuring names don’t start or end with hyphens
Environment-specific configuration
Section titled “Environment-specific configuration”Use external parameters to configure values that differ between development and production environments.
Service discovery
Section titled “Service discovery”In Kubernetes, services discover each other using the cluster’s built-in DNS. A service named api is reachable at api.<namespace>.svc.cluster.local. The generated Helm charts configure service references automatically using Kubernetes-native DNS resolution.
Troubleshooting
Section titled “Troubleshooting”Connection strings empty in Kubernetes
Section titled “Connection strings empty in Kubernetes”If your application can’t find connection strings at runtime, verify that the generated ConfigMaps and Secrets are correctly mounted as environment variables in your pod specifications. Check that the resource names in your Helm values match the expected connection string names.
Password and authentication issues
Section titled “Password and authentication issues”Kubernetes Secrets store values as base64-encoded strings. Verify that your Secrets are properly encoded and that the generated templates reference them correctly. Use kubectl get secret <name> -o yaml to inspect Secret contents.