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
+-- CacheThe 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: 8080The corresponding Service may be another file:
apiVersion: v1
kind: Service
metadata:
name: orders-api
spec:
selector:
app: orders-api
ports:
- port: 80
targetPort: 8080The configuration is valid, but the relationship between application code and infrastructure is separated.
A developer needs to understand both:
C# Application
+
YAML InfrastructureThe 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 PlatformThe 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 InfrastructureThis 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 ServiceWithout 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 ServiceThe 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
+-- PortWith 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 DatabaseThis 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 Definitionfrom:
Environment-Specific SecretsDo 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 Brokerinstead of searching through several deployment files.
The workflow becomes:
Clone Repository
|
v
Open Solution
|
v
Open AppHost
|
v
Understand Application ResourcesThis 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:
Network policies
Storage classes
Ingress
Security policies
RBAC
Pod security configuration
Cluster-specific settings
Infrastructure controllers
Therefore, a realistic architecture may be:
Aspire AppHost
|
+-- Application Resources
|
+-- Service Relationships
|
v
Deployment Configuration
|
+-- Platform-Specific Resources
+-- Security Policies
+-- Infrastructure ControlsThe goal should be to reduce unnecessary duplication, not eliminate every YAML file.
Compare AppHost and Kubernetes YAML
| Area | Aspire AppHost | Kubernetes YAML |
|---|---|---|
| Primary language | C# | YAML |
| Developer familiarity | High for .NET teams | Requires Kubernetes knowledge |
| Application relationships | Explicit in code | Spread across resources |
| Local development | Strong | Requires cluster-oriented setup |
| Kubernetes-specific controls | Limited by abstraction | Very detailed |
| Type checking | C# compiler/tooling | Manifest validation |
| Infrastructure portability | Application-oriented | Kubernetes-oriented |
| Platform-specific configuration | Less direct | Highly 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
Productionand each environment contains similar service definitions.
Copying and modifying YAML can create drift:
Development YAML
|
v
Staging YAML
|
v
Production YAMLSmall 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 DatabaseThe 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
DeployDo 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
UnavailableTest how the application behaves.
The objective is not merely to verify that the database starts.
Verify that the application:
Reports the dependency correctly.
Handles temporary failures.
Recovers when the dependency returns.
Does not expose sensitive connection information.
Does not enter an uncontrolled restart loop.
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 DBThis 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
ApplicationSource 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 Workerwhile production may require:
5 API replicas
3 WorkersThe 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
DeploymentReview whether application changes also require infrastructure changes.
A useful pull-request review should consider:
Application Code
+
AppHost
+
Deployment Configurationas 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
ConnectionVerify 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 BehaviorLook for differences in:
Secrets
Networking
Storage
Identity
Environment variables
Scaling
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
Use AppHost to model distributed application resources and relationships.
Keep application infrastructure definitions close to the application architecture.
Avoid hard-coded production secrets.
Separate application modeling from platform-specific infrastructure.
Keep Kubernetes knowledge within the engineering team.
Validate AppHost changes before deployment.
Test dependency failures.
Test health and readiness behavior.
Review application and infrastructure changes together.
Minimize duplicated environment configuration.
Use meaningful resource names.
Keep platform-specific YAML where it provides necessary control.
Test staging deployments before production.
Document which layer owns each infrastructure concern.
Avoid unnecessary abstraction.
Advantages
Application architecture becomes easier to discover.
C# developers can express resource relationships using familiar language and tooling.
Dependency relationships can be represented directly.
Local distributed application development becomes easier to model.
Code review can cover infrastructure relationships alongside application changes.
Redundant configuration can potentially be reduced.
Disadvantages
AppHost does not eliminate the need for platform knowledge.
Kubernetes-specific features may still require separate configuration.
Deployment behavior can differ from local orchestration.
Teams must understand the abstraction boundaries.
Poorly designed abstractions can hide important operational details.
Existing YAML-based deployment systems may require migration work.
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
ProductionIn 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
WorkersStep 2: Identify Application Relationships
Create a simple dependency map:
API
|
+-- Database
+-- Cache
Worker
|
+-- Database
+-- QueueStep 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:
Startup
Networking
Health
Configuration
Scaling
Failure recovery
Step 7: Remove Redundant Configuration
Only after confirming equivalent behavior should duplicated configuration be removed.
Migration Comparison
| Migration Approach | Risk | Effort | Recommended |
|---|---|---|---|
| Replace everything immediately | High | High | No |
| Keep everything unchanged | Low | Low | Limited value |
| Gradual AppHost adoption | Moderate | Moderate | Yes |
| Hybrid application + platform model | Moderate | Moderate | Often 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 configurationFor example:
Service Dependency
|
v
AppHost
Application Resource
|
v
AppHost
Kubernetes Network Policy
|
v
Kubernetes Configuration
Storage Class
|
v
Kubernetes ConfigurationThis 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.

Jasen FiciPosted Sep 3, 2026, 1:05 PM
We featured this post in DotNetNews here: https://dotnetnews.co/archive/the-net-news-daily-issue-533/