-
Notifications
You must be signed in to change notification settings - Fork 171
Description
Proposed topic or title
Document that WithHealthCheck must be on the emulator container inside RunAsEmulator, not the outer Azure resource
Location in table of contents.
https://aspire.dev/fundamentals/health-checks/
Reason for the article
Summary
The health checks documentation shows how to use WithHealthCheck to associate custom health checks with resources for WaitFor gating. However, it does not cover how this works with Azure resources running as emulators via RunAsEmulator.
Through trial and error, I discovered that WithHealthCheck must be called on the emulator container resource inside the RunAsEmulator callback, not on the outer Azure resource builder. This is a non-obvious but critical distinction for anyone trying to add custom health checks to emulated Azure resources.
The problem
When using RunAsEmulator() for Azure resources (Cosmos DB, Event Hubs, etc.), the emulator container may report as healthy before it's actually ready to accept connections. This is a known issue (see dotnet/aspire#9880, dotnet/aspire#5097). To mitigate this, users need to register custom deep health checks and associate them with the resource so that WaitFor blocks dependent services until the emulator is truly ready.
What does NOT work
Associating the health check with the outer Azure resource:
var cosmos = builder.AddAzureCosmosDB("CosmosDbConnection");
builder.Services.AddHealthChecks()
.AddAsyncCheck("cosmos-connectivity", async ct =>
{
// ... verify actual database connectivity ...
});
cosmos = cosmos.RunAsEmulator(container =>
{
container.WithLifetime(ContainerLifetime.Session);
});
// ❌ This does NOT gate WaitFor — the Function/API starts before the check passes
cosmos.WithHealthCheck("cosmos-connectivity");With this pattern, WaitFor(cosmos) does not wait for the custom health check. Dependent services start before the emulator is ready, causing startup failures.
What DOES work
Associating the health check with the emulator container inside the RunAsEmulator callback:
var cosmos = builder.AddAzureCosmosDB("CosmosDbConnection");
builder.Services.AddHealthChecks()
.AddAsyncCheck("cosmos-connectivity", async ct =>
{
// ... verify actual database connectivity ...
});
cosmos = cosmos.RunAsEmulator(container =>
{
container.WithLifetime(ContainerLifetime.Session);
// ✅ Associate with the container resource, not the outer Azure resource
container.WithHealthCheck("cosmos-connectivity");
});With this pattern, WaitFor(cosmos) correctly waits for the custom health check to return Healthy before starting dependent services.
Why this matters
The RunAsEmulator pattern wraps the Azure resource with an underlying container resource. WaitFor checks health on the container, not the outer Azure resource wrapper. This is not documented anywhere, and the current health checks documentation only shows examples with simple containers and projects — not the RunAsEmulator scenario.
Article abstract
The health checks page should include:
A section or example showing how to add custom health checks to emulated Azure resources using WithHealthCheck inside the RunAsEmulator callback.
A note explaining that when an Azure resource runs as an emulator, health checks must be associated with the emulator container (via the callback parameter), not the outer Azure resource builder.
A practical example like:
// Register the health check in the service collection
builder.Services.AddHealthChecks()
.AddAsyncCheck("cosmos-deep-check", async ct =>
{
var connectionString = await ((IResourceWithConnectionString)cosmos.Resource)
.GetConnectionStringAsync(ct);
if (string.IsNullOrEmpty(connectionString))
return HealthCheckResult.Unhealthy("Connection string not available");
using var client = new CosmosClient(connectionString, new CosmosClientOptions
{
ConnectionMode = ConnectionMode.Gateway,
LimitToEndpoint = true,
});
await client.CreateDatabaseIfNotExistsAsync("mydb", cancellationToken: ct);
return HealthCheckResult.Healthy();
});
// Associate with the emulator container inside RunAsEmulator
cosmos = cosmos.RunAsEmulator(container =>
{
container.WithHealthCheck("cosmos-deep-check");
});
// WaitFor now correctly waits for the deep health check
builder.AddProject<Projects.MyApi>("myapi")
.WaitFor(cosmos);Relevant searches
No response