Introduction
Containerized applications make development and deployment more consistent, but debugging a distributed application inside containers can still be difficult.
A developer may need to inspect several services at the same time:
Frontend
|
v
API
|
+---- PostgreSQL
|
+---- Redis
|
+---- Message Broker
When each service runs inside a separate container, finding the source of a problem can involve logs, ports, environment variables, health checks, and process state.
.NET Aspire is designed to make local distributed application development easier by providing an application model and developer dashboard. Aspire 13.6 expands the container-development experience, including improved ways to work with and debug containerized resources directly from the dashboard.
This article explains how the container debugging workflow works, what developers should inspect when a service fails, and how to use the Aspire dashboard as a central place for troubleshooting distributed .NET applications.
What Is .NET Aspire?
.NET Aspire is a development stack for building and running distributed applications.
Instead of configuring every service independently, developers can describe application resources through an Aspire AppHost.
A simplified application might look like:
AppHost
|
+---- API
|
+---- Web App
|
+---- PostgreSQL
|
+---- Redis
The AppHost describes how these resources relate to each other.
Aspire then provides tooling for running and observing the application during development.
What Is the Aspire Dashboard?
The Aspire Dashboard provides a centralized view of the resources participating in an Aspire application.
Depending on the application configuration, developers can inspect information such as:
Resources
Logs
Traces
Metrics
Resource status
Endpoints
Environment information
Health information
Instead of opening separate terminals for every service, developers can start with one view of the distributed system.
A simplified dashboard workflow looks like:
Aspire Dashboard
|
+---- API
| +---- Logs
| +---- Traces
| +---- Metrics
|
+---- Worker
|
+---- Database
|
+---- Cache
Why Container Debugging Matters
Containers introduce another execution boundary.
A developer might see:
Application works locally
but:
Application fails in container
The difference can come from:
Environment variables
File paths
Network configuration
Port mappings
Container images
Missing dependencies
Runtime versions
Health checks
Container startup order
Debugging therefore requires visibility into both the application and the container environment.
Aspire and Containerized Resources
Aspire applications can include containerized dependencies and application resources.
For example:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres")
.AddDatabase("appdb");
var cache = builder.AddRedis("cache");
builder.AddProject<Projects.Api>("api")
.WithReference(postgres)
.WithReference(cache);
builder.Build().Run();
The AppHost describes the resources rather than requiring every dependency to be started manually.
When the application starts, the Aspire dashboard provides a centralized view of these resources.
What Changes in the Container Debugging Workflow?
Traditional container debugging often starts from the command line.
For example:
docker ps
followed by:
docker logs <container>
and then:
docker exec -it <container> /bin/sh
These commands remain useful, but a distributed application can require many commands across many containers.
The Aspire dashboard provides a higher-level view where developers can identify the failing resource first and then investigate its runtime information.
The workflow becomes:
Run Application
|
v
Open Aspire Dashboard
|
v
Find Failed Resource
|
v
Inspect Logs / Traces / Metrics
|
v
Debug Application or Container
Starting a Containerized Aspire Application
A typical development workflow begins with the AppHost project.
For example:
dotnet run --project AppHost
Aspire starts the configured resources and makes the dashboard available.
The exact startup behavior depends on the AppHost configuration and the resources included in the solution.
Once the dashboard opens, begin by looking at the resource list.
Understanding Resource Status
A healthy application might show:
api Running
worker Running
postgres Running
redis Running
A problem may appear as:
api Failed
worker Running
postgres Running
redis Running
This immediately narrows the investigation.
Instead of debugging the entire application, start with the resource whose state differs from the expected state.
Inspecting Logs
Logs are usually the first place to look when a containerized service fails.
Suppose the API container starts and then exits.
The logs may reveal:
Connection refused
Configuration value missing
Unable to load assembly
Database unavailable
Port already in use
The important advantage of centralized logs is correlation.
You can compare:
API logs
Database logs
Worker logs
instead of opening multiple terminal sessions.
Debugging Application Code
Container debugging becomes more useful when you can move from a runtime failure to the application code responsible for it.
For example:
HTTP Request
|
v
API Container
|
v
Controller
|
v
Service
|
v
Database
Suppose the API returns an unexpected result.
A developer can inspect the relevant application code using the normal .NET debugging workflow while using the Aspire dashboard to understand the surrounding distributed system.
This distinction matters.
The dashboard gives you system visibility.
The debugger gives you code-level inspection.
The two complement each other.
Debugging a Database Connection
One common container problem is database connectivity.
Consider:
API Container
|
| connection string
v
PostgreSQL Container
If the API cannot connect, inspect the following:
Is PostgreSQL running?
Is the database resource healthy?
Is the API referencing the correct resource?
Is the connection string being injected correctly?
Is the application using the correct database name?
Is the expected endpoint available?
With Aspire resource references, connection information can be managed as part of the application model rather than manually copied into multiple configuration files.
Debugging Redis and Other Dependencies
The same approach applies to Redis and other containerized services.
For example:
var redis = builder.AddRedis("cache");
builder.AddProject<Projects.Api>("api")
.WithReference(redis);
If the API reports cache connection failures, first inspect the Redis resource.
This is faster than assuming the problem is inside application code.
The dependency itself may have failed to start.
Environment Variables
Container problems are frequently configuration problems.
An application may expect:
DATABASE_HOST
DATABASE_NAME
CACHE_ENDPOINT
API_KEY
but the container may not receive the expected values.
When debugging, compare:
Developer Environment
|
v
AppHost Configuration
|
v
Container Environment
|
v
Application Configuration
A missing environment variable can cause an application to fail before the first HTTP request is processed.
Port Problems
Port configuration is another common source of confusion.
A service may listen on one port inside the container while a different port is exposed to the host.
Conceptually:
Host
Port 5000
|
v
Container
Port 8080
Do not assume the host port and container port are identical.
When an endpoint does not respond, inspect the resource's endpoint configuration rather than changing ports randomly.
Health Checks
Health checks help distinguish between a running container and a usable application.
For example:
Container: Running
Application: Unhealthy
These are different states.
A container can be alive while the application inside it cannot connect to a database or complete its startup process.
Health information can therefore shorten troubleshooting by identifying dependencies that are not ready.
Distributed Tracing
Logs tell you what individual services reported.
Traces help show how a request moved through multiple services.
For example:
HTTP Request
|
v
API
|
v
Order Service
|
v
Database
If the request becomes slow, tracing can help identify which part of the request path consumed the time.
This becomes increasingly useful as the number of services grows.
Metrics and Container Behavior
Metrics provide another view of the application.
A service may technically be healthy but show:
High CPU
Increasing memory usage
Unusual request latency
Growing request volume
Dependency failures
A dashboard that combines logs, traces, and metrics gives developers more context than logs alone.
A Practical Debugging Scenario
Imagine an Aspire application with:
Web
|
v
API
|
+---- PostgreSQL
|
+---- Redis
The web application reports that orders cannot be loaded.
Start with the dashboard.
Step 1: Check Resource State
Web Running
API Running
Postgres Running
Redis Running
No obvious container has failed.
Step 2: Inspect API Logs
You find:
Timeout while executing database query
Step 3: Inspect Database
PostgreSQL is running, but metrics indicate unusually high resource consumption.
Step 4: Inspect the Trace
The request spends most of its time waiting for the database.
Step 5: Debug Application Code
Inspect the query and determine whether it is missing an appropriate index or processing too much data.
The problem was not a container startup failure. It was an application performance issue visible through the distributed runtime.
This is where a centralized dashboard becomes valuable.
Containers vs Local Processes
Aspire applications can involve a mixture of resources.
For example:
API Local Process
Worker Container
PostgreSQL Container
Redis Container
Frontend Local Process
The dashboard provides a common view even though the underlying execution models differ.
This is useful during development because teams do not need to mentally separate every service into a different debugging workflow.
When to Use Container Debugging
Container debugging is particularly useful when the issue appears only after containerization.
Examples include:
Different environment variables
Missing files
Incorrect working directory
Different runtime versions
Network connectivity issues
Port mapping problems
Container startup failures
Dependency readiness problems
OS-level differences
If the application behaves correctly outside the container but fails inside it, inspect the container environment before rewriting application logic.
Common Mistakes
Debugging the Wrong Resource
Start with resource status and logs before changing application code.
Assuming Running Means Healthy
A running container can still contain an unhealthy application.
Ignoring Dependency Failures
An API may fail because PostgreSQL or Redis failed earlier.
Hardcoding Container Addresses
Use Aspire resource references and configuration mechanisms rather than manually maintaining container IP addresses.
Treating Production and Development as Identical
Local Aspire environments are designed for development workflows. Production container orchestration may have different networking, security, scaling, and observability requirements.
Changing Multiple Things at Once
Change one configuration or code path at a time so you can identify the actual cause.
Troubleshooting Checklist
When a containerized Aspire resource fails, follow this sequence:
[ ] Check resource status
[ ] Inspect startup logs
[ ] Check health status
[ ] Verify dependencies
[ ] Check environment variables
[ ] Check endpoints and ports
[ ] Inspect traces
[ ] Review metrics
[ ] Attach a debugger when appropriate
[ ] Reproduce the failure
[ ] Fix the smallest relevant component
[ ] Restart and verify
This approach prevents random configuration changes from making the original problem harder to diagnose.
Best Practices
Keep the AppHost configuration readable.
Give resources meaningful names.
Use resource references instead of hardcoded dependency addresses.
Check logs before changing code.
Use health checks for important dependencies.
Use traces for distributed request problems.
Use metrics to investigate performance issues.
Test containerized behavior early in development.
Keep development and production assumptions separate.
Document custom container configuration.
Keep container images and runtimes current.
Reproduce issues with the same configuration used by the failing environment.
Advantages and Disadvantages
Advantages
Centralized resource visibility
Easier log inspection
Better understanding of distributed dependencies
Faster identification of failed resources
Useful combination of logs, traces, and metrics
Simplifies local distributed application development
Reduces the need to manage every service independently
Disadvantages
Adds another development abstraction
Does not replace application-level debugging
Container-specific problems still require container knowledge
Production environments may behave differently
Complex distributed applications still require systematic troubleshooting
Summary
.NET Aspire's dashboard provides a useful control point for debugging distributed applications that include containerized resources.
Instead of starting with individual container commands, developers can begin with resource status, logs, traces, metrics, endpoints, and health information. This makes it easier to determine whether a failure originates in application code, a dependency, configuration, networking, or the container environment itself.
The most effective workflow combines Aspire's distributed-system visibility with normal .NET debugging tools. Use the dashboard to understand what is happening across the application, then move into code-level debugging when the failing component has been identified.
For teams building containerized .NET applications, this approach can make local debugging considerably more structured and repeatable.

Join the conversation! Your thoughts help the community grow.