Introduction
When working with distributed applications, developers usually have several terminals open at the same time.
One terminal runs the API. Another runs a worker. Another shows database logs. If a service has multiple replicas, things become even more confusing because each replica may have its own process and output.
.NET Aspire 13.5 addresses part of this problem by bringing an interactive terminal directly into the Aspire dashboard.
With the new WithTerminal() capability, a resource can expose an interactive terminal session that developers can use from the dashboard. Aspire 13.5 also allows developers to switch between replicas and supports multiple viewers attaching to the same terminal session. The matching aspire terminal CLI commands can be used to list and attach to terminal sessions. These terminal APIs are experimental in Aspire 13.5.
This is particularly interesting for applications that run multiple instances of the same resource.
Instead of:
Terminal 1 → API Replica 1
Terminal 2 → API Replica 2
Terminal 3 → API Replica 3
the developer can work through the Aspire dashboard:
Aspire Dashboard
|
v
Orders API
|
+---- Replica 1
+---- Replica 2
+---- Replica 3
|
v
Interactive Terminal
The feature is not really about replacing terminals. It is about making interactive distributed resources easier to discover, access, and manage.
What Is the Aspire Dashboard Terminal?
Traditional Aspire resources generally expose information through logs and telemetry.
For example:
Orders API
|
+--> Console Logs
+--> Traces
+--> Metrics
But some applications do not behave like ordinary background services.
They may require:
Standard input
Interactive shells
REPL commands
Terminal-based user interfaces
Interactive debugging tools
Developer utilities
Aspire 13.5 adds a terminal experience for these scenarios.
The AppHost can configure a resource with:
.WithTerminal()
The resource can then be accessed through an interactive terminal in the dashboard. Microsoft describes this as a way to run shells, REPLs, terminal user interfaces, and other applications that expect real stdin and stdout.
Why Interactive Terminals Matter in Distributed Applications
Consider an application with three API replicas:
Orders API
|
+--> Replica 1
+--> Replica 2
+--> Replica 3
Suppose Replica 2 is behaving differently from the others.
Without an integrated terminal, a developer may need to determine:
Which process?
Which terminal?
Which port?
Which replica?
Which logs?
That creates unnecessary friction.
The Aspire dashboard already knows about the resources and their replicas, so bringing the terminal into the same interface creates a more connected development experience.
The workflow becomes:
Dashboard
|
v
Orders API
|
v
Select Replica 2
|
v
Open Terminal
|
v
Investigate
This is particularly useful during local debugging and development.
Understanding Aspire Replicas
Aspire supports running multiple replicas of a resource using the WithReplicas API.
For example:
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.OrdersApi>("orders-api")
.WithReplicas(3);
builder.Build().Run();
The exact project type and AppHost structure depend on the application, but the important concept is that Aspire can represent multiple instances of the same logical resource.
The dashboard represents the parent resource and its individual replicas separately. Each replica has its own resource identity and logs.
Conceptually:
orders-api
|
+-- orders-api-replica-1
+-- orders-api-replica-2
+-- orders-api-replica-3
This gives developers a way to inspect individual instances instead of treating the entire service as one undifferentiated process.
Adding a Terminal to a Resource
The key API introduced for this workflow is:
.WithTerminal()
A simplified AppHost example is:
var builder = DistributedApplication.CreateBuilder(args);
var worker = builder.AddProject<Projects.Worker>("worker")
.WithTerminal();
builder.Build().Run();
Once the application starts, the resource can expose an interactive terminal through the Aspire dashboard.
The important thing to understand is that the terminal belongs to the resource's process.
It is not simply another console-log viewer.
A log viewer is generally:
Process
|
v
stdout/stderr
|
v
Dashboard
An interactive terminal is:
Developer
|
v
Dashboard Terminal
|
+--> stdin
|
+--> Process
|
+<-- stdout
|
+<-- stderr
That distinction makes the feature useful for applications that actually require interaction.
Terminal vs Console Logs
These two capabilities solve different problems.
| Feature | Console Logs | Interactive Terminal |
|---|---|---|
| View stdout | Yes | Yes |
| View stderr | Yes | Yes |
| Send input | No | Yes |
| Interactive commands | No | Yes |
| REPL applications | No | Yes |
| Terminal UI | No | Yes |
| Follow process output | Yes | Yes |
| Switch replicas | Resource-based | Supported |
| Multiple viewers | Log viewing | Terminal sessions can have multiple viewers |
Aspire's dashboard already provides resource logs and telemetry. The terminal extends that experience to applications that require interactive input.
Multi-Replica Terminal Workflow
Now consider a service with three replicas:
orders-api
│
├── replica-1
├── replica-2
└── replica-3
Suppose each replica has an interactive process.
The dashboard can provide a way to switch between replicas:
+--------------------------------+
| Orders API |
| |
| Replica: [ replica-2 ▼ ] |
| |
| > diagnostic command |
| > status |
| |
| Process output... |
+--------------------------------+
This is useful when a problem appears only on one instance.
Instead of opening several terminal windows and manually tracking which process belongs to which replica, the resource hierarchy provides the context.
Microsoft specifically calls out replica switching as part of the 13.5 terminal experience.
Multiple Viewers
Another interesting capability is that multiple viewers can attach to the same terminal session.
For example:
Terminal Session
|
+---------+---------+
| |
v v
Dashboard Tab Local Terminal
This can be useful during collaborative debugging.
One developer might be looking at the Aspire dashboard while another connects from a local terminal.
The important point is that both can observe the same interactive process rather than creating separate processes simply because they need different access points. Microsoft explicitly describes multiple viewers attaching to a terminal session in Aspire 13.5.
Using the Aspire CLI
The dashboard is not the only way to access these sessions.
Aspire 13.5 also introduces matching terminal commands through the CLI.
Microsoft's release notes describe commands that can list terminal sessions and attach to them. The terminal command feature must be enabled because the terminal APIs are experimental in this release.
Conceptually, the workflow becomes:
aspire terminal list
Then:
aspire terminal attach <session>
The exact command syntax and feature-flag requirements should be checked against the Aspire 13.5 CLI documentation because the terminal functionality is experimental.
This gives developers two access patterns:
Dashboard
|
+--> Interactive Terminal
CLI
|
+--> Attach to Terminal Session
That flexibility is useful for developers who prefer terminal-first workflows.
Building a Simple Interactive Resource
A good example is a small command-line application.
Imagine a .NET application that asks the developer for a number:
Console.Write("Enter a number: ");
var input = Console.ReadLine();
if (int.TryParse(input, out var number))
{
Console.WriteLine($"You entered: {number}");
}
else
{
Console.WriteLine("Invalid number.");
}
Normally, this application expects a real terminal.
If it is modeled as an Aspire resource with terminal support, the developer can interact with it from the Aspire dashboard rather than opening a completely separate terminal window.
That makes it a simple demonstration of why stdin support matters.
A More Realistic Developer Tool
The same concept becomes more useful with a development utility.
For example:
Database Utility
> migrate
> seed
> reset
> status
> exit
The application might contain:
while (true)
{
Console.Write("> ");
var command = Console.ReadLine();
switch (command)
{
case "status":
Console.WriteLine("Database is ready.");
break;
case "seed":
await SeedDatabaseAsync();
Console.WriteLine("Seed completed.");
break;
case "exit":
return;
default:
Console.WriteLine("Unknown command.");
break;
}
}
With an interactive terminal, such a tool can become part of the Aspire development environment.
Instead of documenting:
Open another terminal.
Navigate to this directory.
Run this command.
Set this environment variable.
the developer can access the resource directly from Aspire.
Benchmarking the Terminal Experience
The article title uses the word "benchmarking," but this should not be interpreted as a claim that the dashboard terminal is faster than a traditional terminal.
There is no meaningful reason to invent a universal latency benchmark for this feature.
Instead, benchmark the developer workflow.
Useful measurements include:
| Measurement | What to Observe |
|---|---|
| Session startup | How quickly a terminal becomes available |
| Replica switching | Time required to move between instances |
| Input responsiveness | Whether commands feel interactive |
| Output rendering | Whether output remains readable |
| Reconnection | Behavior after dashboard reconnects |
| Multiple viewers | Behavior when several clients attach |
| CLI attach | Ease of moving from dashboard to terminal |
| Resource discovery | How quickly developers identify the correct replica |
These measurements are more useful than simply measuring terminal throughput.
Testing Replica Switching
A useful test setup might use three replicas:
var api = builder.AddProject<Projects.OrdersApi>("orders-api")
.WithReplicas(3)
.WithTerminal();
Then verify:
Replica 1
|
+--> Terminal opens
+--> Input works
+--> Output visible
Replica 2
|
+--> Terminal opens
+--> Input works
+--> Output visible
Replica 3
|
+--> Terminal opens
+--> Input works
+--> Output visible
The goal is not to prove that all replicas are identical.
It is to verify that the developer can reliably identify and interact with the intended instance.
Why Replica-Specific Debugging Matters
Distributed applications often behave differently across instances.
For example:
Replica 1 → Healthy
Replica 2 → Slow
Replica 3 → Healthy
The cause could be:
Different startup state
Temporary external dependency failure
Replica-specific data
Configuration differences
Resource pressure
A timing-related problem
A centralized dashboard with replica-aware terminal access makes the debugging workflow more direct.
The developer can:
Identify replica
↓
Open terminal
↓
Inspect state
↓
Run diagnostic command
↓
Compare with another replica
Terminal Sessions and Resource Identity
One important design principle is to keep the resource identity clear.
Suppose the dashboard shows:
payments-api
and:
payments-api-abc123
payments-api-def456
payments-api-ghi789
A developer should be able to understand that the child entries represent individual replicas.
Aspire's dashboard already uses this model for resource logs and telemetry. Replicated resources are represented under a parent resource with individual replica entries.
This same mental model makes terminal access easier to understand.
Terminal Sessions Are Not Debuggers
Another important distinction is that an interactive terminal is not the same thing as an IDE debugger.
Aspire 13.5's release notes explicitly state that resources using the terminal currently run as plain processes, so developers should attach the debugger manually when debugging is required.
Therefore:
Terminal
|
+--> stdin/stdout interaction
+--> commands
+--> REPL
+--> diagnostics
is different from:
Debugger
|
+--> Breakpoints
+--> Step Into
+--> Step Over
+--> Variables
+--> Call Stack
Do not expect WithTerminal() to replace your normal debugger.
Common Mistakes
Mistake 1: Treating the Terminal as a Log Viewer
A terminal exists for interactive input and output.
If the application only produces logs, the standard dashboard console is usually enough.
Mistake 2: Assuming Every Resource Needs a Terminal
Adding terminal support to every resource can make the development environment harder to understand.
Use it where interactive behavior provides real value.
Mistake 3: Expecting Debugger Integration
The terminal is not a replacement for Visual Studio, VS Code, or another debugger. Aspire 13.5 currently requires manual debugger attachment for terminal resources.
Mistake 4: Ignoring Replica Identity
When debugging multiple replicas, always verify which instance you are interacting with.
Mistake 5: Treating Experimental APIs as Stable
WithTerminal() and the corresponding CLI commands are experimental in Aspire 13.5.
Troubleshooting
| Problem | What to Check |
|---|---|
| Terminal does not appear | Verify .WithTerminal() is configured |
| Input does not work | Confirm the application actually reads stdin |
| Wrong replica selected | Check the resource hierarchy |
| Terminal disconnects | Check dashboard/session state |
| CLI attach unavailable | Verify the terminal CLI feature is enabled |
| Debugging does not work | Attach the debugger manually |
| Multiple viewers behave unexpectedly | Verify the terminal session and viewer configuration |
| Resource behaves differently per replica | Inspect each replica individually |
Advantages
One Place for Distributed Development
The dashboard becomes a central place for resources, logs, telemetry, and interactive terminals.
Easier Replica Debugging
Developers can move between replicas without manually tracking multiple terminal windows.
Better Developer Experience
Interactive applications become easier to discover and use.
Multiple Viewer Support
More than one client can observe the same terminal session, which can help during collaborative troubleshooting.
Dashboard and CLI Access
Developers can choose between the graphical dashboard and terminal-based workflow.
Disadvantages and Limitations
Experimental API
The terminal APIs are experimental in Aspire 13.5, so teams should expect changes as the feature evolves.
Not a Debugger
Developers still need normal debugger tooling for breakpoints and step-through debugging.
Not Necessary for Every Service
Most standard web APIs do not need interactive stdin.
More Resource Complexity
Adding interactive capabilities can make an AppHost more complex if used without a clear reason.
Workflow Still Needs Validation
Teams should test reconnection, replica selection, and multi-viewer behavior before depending heavily on the feature.
Best Practices
Use Terminals for Truly Interactive Resources
Good candidates include:
REPL applications
CLI tools
Terminal UIs
Interactive workers
Development utilities
Shell-based diagnostics
Keep Standard Services Simple
If an API only produces logs and telemetry, use the existing Aspire dashboard features.
Make Replica Identity Obvious
When multiple replicas exist, make sure developers can easily determine which instance they are interacting with.
Keep Debugging Tools Separate
Use the terminal for interaction and the debugger for debugging.
Test the Workflow With Multiple Replicas
Do not assume single-replica behavior represents a multi-replica environment.
Be Careful With Experimental Features
Because terminal support is experimental in 13.5, isolate usage behind a clear development strategy and be prepared for API changes.
A Practical Multi-Replica Example
A simple AppHost could look like:
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.OrdersApi>("orders-api")
.WithReplicas(3)
.WithTerminal();
var worker = builder.AddProject<Projects.OrderWorker>("order-worker")
.WithTerminal();
builder.Build().Run();
The resulting development environment could conceptually look like:
Aspire Dashboard
│
├── orders-api
│ ├── replica-1
│ │ └── Terminal
│ ├── replica-2
│ │ └── Terminal
│ └── replica-3
│ └── Terminal
│
└── order-worker
└── Terminal
This is where the feature starts to become genuinely useful.
The dashboard is no longer only showing the distributed system. It becomes an interactive control surface for development resources.
Conclusion
Aspire 13.5's dashboard terminal is a useful improvement for developers working with interactive processes and multiple replicas. The biggest benefit is not that it eliminates the normal terminal, but that it brings interactive resources into the same place where developers already see their services, logs, and telemetry. Being able to switch between replicas and have multiple viewers attach to a session can make certain debugging and development workflows much easier to manage. At the same time, the feature is still experimental, and it should not be treated as a replacement for a proper debugger or as something every resource needs. For teams building distributed .NET applications with CLI tools, REPLs, terminal-based utilities, or replica-specific troubleshooting needs, WithTerminal() is worth experimenting with and evaluating as part of the local development experience.
Join the conversation! Your thoughts help the community grow.