Introduction

Infrastructure configuration has traditionally been expressed through YAML files. Kubernetes deployments, services, configuration, networking, and other resources are commonly described through separate configuration files.

This approach works well, but large distributed applications can accumulate a significant amount of infrastructure configuration.

A typical project might contain:

.github/
    workflows/

k8s/
    deployment.yaml
    service.yaml
    configmap.yaml
    ingress.yaml
    secret.yaml

src/
    Orders.Api/
    Orders.Worker/
    Notifications/

The application code is written in C#, while much of the application infrastructure is described somewhere else.

.NET Aspire takes a different approach for distributed .NET applications. The AppHost provides a code-based model for describing application resources and their relationships.

With Aspire 13.5, the AppHost can become an important part of a code-first infrastructure workflow, particularly when the application needs to move between local orchestration and deployment environments.

The important question is not whether C# can replace every infrastructure YAML file.

It is:

Can an Aspire AppHost provide a more maintainable application infrastructure model while still giving teams the deployment control they need?

What Is an Aspire AppHost?

The AppHost is the orchestration project for an Aspire distributed application.

A simple application might look like:

AppHost
   |
   +-- Orders API
   +-- Orders Worker
   +-- Database
   +-- Cache

The AppHost describes these resources and their relationships.

For example:

var builder = DistributedApplication.CreateBuilder(args);

var database = builder.AddPostgres("postgres")
    .AddDatabase("ordersdb");

var cache = builder.AddRedis("cache");

var api = builder.AddProject<Projects.OrdersApi>("orders-api")
    .WithReference(database)
    .WithReference(cache);

builder.AddProject<Projects.OrderWorker>("orders-worker")
    .WithReference(database);

builder.Build().Run();

Instead of manually maintaining relationships across several configuration files, the application architecture is visible in one code-based model.

Traditional YAML-Based Deployment

A Kubernetes deployment commonly separates application resources.

For example:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: orders-api
  template:
    metadata:
      labels:
        app: orders-api
    spec:
      containers:
        - name: orders-api
          image: orders-api:latest
          ports:
            - containerPort: 8080

The corresponding Service may be another file:

apiVersion: v1
kind: Service
metadata:
  name: orders-api
spec:
  selector:
    app: orders-api
  ports:
    - port: 80
      targetPort: 8080

The configuration is valid, but the relationship between application code and infrastructure is separated.

A developer needs to understand both:

C# Application
      +
YAML Infrastructure

The Code-First Alternative

With an AppHost, the application structure can be expressed in C#:

var builder = DistributedApplication.CreateBuilder(args);

var api = builder.AddProject<Projects.OrdersApi>(
    "orders-api");

var worker = builder.AddProject<Projects.OrdersWorker>(
    "orders-worker");

worker.WithReference(api);

builder.Build().Run();

The relationship is visible directly in the application model.

This can make the architecture easier to discover, particularly for .NET developers who are already comfortable with C#.

Infrastructure and Application Code Are Still Different

Code-first infrastructure does not mean infrastructure concerns disappear.

There are still two conceptual layers:

Application Model
       |
       v
Infrastructure Model
       |
       v
Deployment Platform

The AppHost describes the distributed application.

The target platform still determines how resources are ultimately provisioned and executed.

For example:

Aspire AppHost
      |
      v
Application Resources
      |
      v
Deployment Target
      |
      +-- Kubernetes
      +-- Container Environment
      +-- Cloud Infrastructure

This distinction is important when evaluating whether AppHost can replace existing deployment YAML.

When AppHost Provides the Most Value

An AppHost is particularly useful when the application contains several related services.

Consider:

E-Commerce Application

API
 |
 +-- PostgreSQL
 +-- Redis
 +-- Message Broker
 |
 +-- Order Worker
       |
       +-- Notification Service

Without a centralized application model, developers may need to inspect several configuration files to understand these relationships.

With an AppHost:

AppHost
 |
 +-- PostgreSQL
 +-- Redis
 +-- Message Broker
 +-- API
 +-- Worker
 +-- Notification Service

The architecture becomes easier to inspect.

Define Resources With C#

The AppHost can define infrastructure resources using C# APIs.

For example:

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("postgres");

var database = postgres.AddDatabase("orders");

var redis = builder.AddRedis("redis");

builder.AddProject<Projects.OrdersApi>("orders-api")
    .WithReference(database)
    .WithReference(redis);

builder.Build().Run();

The important part is not the number of lines saved.

The value is that resource relationships become part of the application's orchestration model.

Configuration Becomes More Explicit

Suppose an API needs a database connection.

Traditional configuration might require several pieces:

Deployment YAML
     |
     +-- Environment Variable
     +-- Secret
     +-- Service Name
     +-- Port

With Aspire, the relationship can be expressed directly:

var database = postgres.AddDatabase("orders");

builder.AddProject<Projects.OrdersApi>("orders-api")
    .WithReference(database);

The application model now explicitly communicates:

Orders API
    |
    v
Orders Database

This reduces the chance that the application documentation and deployment configuration drift apart.

Use Parameters for Environment-Specific Values

Not every infrastructure value should be hard-coded.

For example:

var builder = DistributedApplication.CreateBuilder(args);

var databasePassword =
    builder.AddParameter("database-password", secret: true);

var postgres = builder.AddPostgres("postgres")
    .WithPassword(databasePassword);

var database =
    postgres.AddDatabase("orders");

builder.AddProject<Projects.OrdersApi>("orders-api")
    .WithReference(database);

builder.Build().Run();

The exact configuration should follow the security requirements of the environment.

The important principle is to separate:

Application Definition

from:

Environment-Specific Secrets

Do not place production credentials directly into source code.

AppHost Can Improve Developer Onboarding

One advantage of an application-centric infrastructure model is discoverability.

A new developer can inspect the AppHost and see:

Orders API
Orders Worker
PostgreSQL
Redis
Message Broker

instead of searching through several deployment files.

The workflow becomes:

Clone Repository
      |
      v
Open Solution
      |
      v
Open AppHost
      |
      v
Understand Application Resources

This does not eliminate the need for infrastructure knowledge, but it can reduce the initial learning curve for application developers.

YAML Still Has an Important Role

A common mistake is treating code-first infrastructure as a universal replacement for YAML.

Kubernetes has a large ecosystem of configuration and operational resources.

Organizations may still need explicit configuration for:

Therefore, a realistic architecture may be:

Aspire AppHost
      |
      +-- Application Resources
      |
      +-- Service Relationships
      |
      v
Deployment Configuration
      |
      +-- Platform-Specific Resources
      +-- Security Policies
      +-- Infrastructure Controls

The goal should be to reduce unnecessary duplication, not eliminate every YAML file.

Compare AppHost and Kubernetes YAML

AreaAspire AppHostKubernetes YAML
Primary languageC#YAML
Developer familiarityHigh for .NET teamsRequires Kubernetes knowledge
Application relationshipsExplicit in codeSpread across resources
Local developmentStrongRequires cluster-oriented setup
Kubernetes-specific controlsLimited by abstractionVery detailed
Type checkingC# compiler/toolingManifest validation
Infrastructure portabilityApplication-orientedKubernetes-oriented
Platform-specific configurationLess directHighly flexible

Neither approach is universally better.

The right choice depends on the application's architecture and deployment requirements.

Replace Duplication, Not Kubernetes Knowledge

Suppose the same application has:

Development
Staging
Production

and each environment contains similar service definitions.

Copying and modifying YAML can create drift:

Development YAML
      |
      v
Staging YAML
      |
      v
Production YAML

Small changes can become inconsistent.

A centralized application model can reduce duplication for resources that share the same logical architecture.

However, environment-specific infrastructure should still remain environment-specific.

Model Relationships Instead of Infrastructure Details

A useful design principle is to describe what the application needs.

For example:

var database =
    postgres.AddDatabase("orders");

builder.AddProject<Projects.OrdersApi>(
    "orders-api")
    .WithReference(database);

This communicates:

Orders API needs Orders Database

The application model does not need to contain every underlying infrastructure implementation detail.

That separation makes the AppHost easier to maintain.

Test Configuration Before Deployment

Code-based infrastructure still needs validation.

A good workflow is:

Modify AppHost
      |
      v
Build
      |
      v
Run Application
      |
      v
Validate Resources
      |
      v
Test Dependencies
      |
      v
Deploy

Do not wait until production deployment to discover that a resource relationship is incorrect.

Test Dependency Failures

Distributed applications need to behave predictably when dependencies are unavailable.

For example:

Orders API
    |
    v
PostgreSQL
    |
    X
Unavailable

Test how the application behaves.

The objective is not merely to verify that the database starts.

Verify that the application:

Test Configuration Drift

One useful experiment is to compare the application model against deployment configuration.

For example:

AppHost:
Orders API -> Orders DB

Deployment:
Orders API -> Legacy DB

This is configuration drift.

The application's logical architecture and deployed architecture no longer match.

A code-first model is valuable only if the deployment process actually respects that model.

Test Resource Naming

Resource names become important when they are referenced by other services.

For example:

var database =
    postgres.AddDatabase("orders-db");

Then:

builder.AddProject<Projects.OrdersApi>("orders-api")
    .WithReference(database);

Names should be consistent and meaningful.

Avoid unnecessary renaming between environments because resource identity can become difficult to troubleshoot.

Test Secrets Carefully

Infrastructure code often makes configuration easier to centralize, but secrets require special treatment.

Avoid:

var password = "ProductionPassword123";

Instead, use secure parameterization and environment-specific secret management.

The desired architecture is:

AppHost
   |
   v
Secret Reference
   |
   v
Secure Environment
   |
   v
Application

Source control should not become a secret store.

Test Scaling Requirements

Application resource definitions and production scaling requirements are not always identical.

A development application may run:

1 API
1 Worker

while production may require:

5 API replicas
3 Workers

The deployment process must preserve the logical application model while allowing operational scaling.

This is another reason not to treat the AppHost as a literal replacement for every platform-level configuration.

Test Health and Readiness

Distributed applications should expose useful health information.

For example:

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/health");

app.Run();

The deployment platform can then use the appropriate health mechanism.

The AppHost describes the application resource.

The target platform remains responsible for operating the workload correctly.

Test Application Updates

A code-first infrastructure model should also be tested during normal development.

For example:

Version 1
   |
   v
AppHost Configuration
   |
   v
Deployment

Version 2
   |
   v
Changed Resource Relationship
   |
   v
Deployment

Review whether application changes also require infrastructure changes.

A useful pull-request review should consider:

Application Code
+
AppHost
+
Deployment Configuration

as one change when the architecture changes.

Common Mistakes

Treating AppHost as a Complete Kubernetes Replacement

Platform-specific requirements may still need explicit Kubernetes configuration.

Hard-Coding Secrets

Code-based configuration does not make secrets safe.

Copying Infrastructure Logic Into Multiple Projects

The purpose of a centralized AppHost is to reduce unnecessary duplication.

Ignoring Production Differences

Development orchestration and production infrastructure have different requirements.

Assuming Compilation Means Deployment Is Correct

A C# AppHost can compile while the resulting deployment still has operational problems.

Removing Kubernetes Knowledge From the Team

Teams deploying to Kubernetes still need to understand Kubernetes behavior.

Over-Abstraction

Hiding every infrastructure detail can make production troubleshooting harder.

Troubleshooting

Resource Is Defined but Application Cannot Connect

Check:

Resource Definition
       |
       v
Reference
       |
       v
Configuration
       |
       v
Connection

Verify that the dependency is actually referenced by the consuming project.

Local Environment Works but Deployment Fails

Compare:

Local Environment
        |
        v
AppHost Behavior

Production Environment
        |
        v
Deployment Behavior

Look for differences in:

Deployment Contains Different Resources

Compare the logical application model with the generated or deployed configuration.

If they differ, identify where the divergence occurred.

Production Requires a Kubernetes Feature Not Represented by AppHost

Keep the platform-specific configuration.

The goal is not to force every infrastructure concern into C#.

Best Practices

  1. Use AppHost to model distributed application resources and relationships.

  2. Keep application infrastructure definitions close to the application architecture.

  3. Avoid hard-coded production secrets.

  4. Separate application modeling from platform-specific infrastructure.

  5. Keep Kubernetes knowledge within the engineering team.

  6. Validate AppHost changes before deployment.

  7. Test dependency failures.

  8. Test health and readiness behavior.

  9. Review application and infrastructure changes together.

  10. Minimize duplicated environment configuration.

  11. Use meaningful resource names.

  12. Keep platform-specific YAML where it provides necessary control.

  13. Test staging deployments before production.

  14. Document which layer owns each infrastructure concern.

  15. Avoid unnecessary abstraction.

Advantages

Disadvantages

A Practical Hybrid Architecture

For many teams, a hybrid model is more realistic than a complete replacement.

                    .NET Aspire AppHost
                           |
          +----------------+----------------+
          |                                 |
          v                                 v
 Application Resources              Resource Relationships
          |                                 |
          +----------------+----------------+
                           |
                           v
                    Deployment Layer
                           |
             +-------------+-------------+
             |                           |
             v                           v
       Kubernetes YAML             Platform Controls
             |                           |
             +-------------+-------------+
                           |
                           v
                     Production

In this model, Aspire owns the application-oriented architecture while Kubernetes remains responsible for platform-specific concerns.

Example Migration Strategy

Teams currently using extensive YAML can migrate incrementally.

Step 1: Inventory Existing Resources

Identify:

Deployments
Services
ConfigMaps
Secrets
Databases
Caches
Workers

Step 2: Identify Application Relationships

Create a simple dependency map:

API
 |
 +-- Database
 +-- Cache

Worker
 |
 +-- Database
 +-- Queue

Step 3: Model the Application in AppHost

Represent the resources and relationships that belong to the application architecture.

Step 4: Keep Platform-Specific Configuration

Do not remove Kubernetes configuration that provides infrastructure capabilities not represented by the application model.

Step 5: Deploy to Staging

Validate the generated or resulting deployment.

Step 6: Compare Behavior

Test:

Step 7: Remove Redundant Configuration

Only after confirming equivalent behavior should duplicated configuration be removed.

Migration Comparison

Migration ApproachRiskEffortRecommended
Replace everything immediatelyHighHighNo
Keep everything unchangedLowLowLimited value
Gradual AppHost adoptionModerateModerateYes
Hybrid application + platform modelModerateModerateOften practical

The safest strategy is usually incremental adoption.

A Practical Decision Framework

Before replacing deployment YAML, ask:

Does this configuration describe
application architecture?
        |
        +-- Yes --> Consider AppHost
        |
        +-- No --> Keep platform configuration

For example:

Service Dependency
        |
        v
AppHost

Application Resource
        |
        v
AppHost

Kubernetes Network Policy
        |
        v
Kubernetes Configuration

Storage Class
        |
        v
Kubernetes Configuration

This keeps each concern in the appropriate layer.

Conclusion

.NET Aspire 13.5 provides a strong opportunity to move more of the distributed application's architecture into a code-first AppHost model. For .NET teams, expressing services, dependencies, databases, caches, and other application resources in C# can make the system easier to understand and maintain.

However, replacing deployment YAML should not be treated as an all-or-nothing migration.

Kubernetes still provides platform-level capabilities that an application orchestration model should not attempt to hide. Security policies, networking controls, storage behavior, cluster configuration, and other operational concerns may continue to require platform-specific configuration.

The most practical approach is to let the Aspire AppHost describe the application and its relationships, while the deployment platform remains responsible for infrastructure-specific behavior. This hybrid model can reduce configuration duplication without sacrificing the operational control required by production Kubernetes environments.