We keep seeing the same pattern after a .NET 10 cutover: an app pool recycles, the load balancer or external monitor hammers `/health`, and every probe opens a fresh SqlConnection (or worse, spins EF Core and runs a query). Thirty workers × a five-second interval is enough to push a shared SQL tier into pool waits while the site still looks “up” in the monitor.

The failure mode is not a bad health library. It is one endpoint doing two jobs. Liveness should answer “is the process accepting HTTP?” Readiness should answer “can this instance take traffic that needs SQL?” If you fuse them, every probe competes with real requests for the same connection pool—especially right after an IIS recycle when the pool is cold and ANCM is bringing Kestrel up.

#What burned us on a shared IIS box

A customer wired `AddDbContextCheck` into the only health route and pointed both the platform probe and an uptime robot at it. During a normal app-pool recycle wave, probes overlapped with warmup traffic. SQL showed a spike in connection opens and `timeout expired` from the pool, not from slow queries. The site returned 503s to users while `/health` flipped between 200 and 503 depending on who won the race for a free connection. Turning the SQL check off the public path stopped the storm in minutes.

Opinionated rule: the endpoint your load balancer or keep-alive probe hits must not open SQL, hit Redis, or call a downstream HTTP API. Process up is enough. Dependency checks belong on a separate readiness route you call from deploy gates or internal monitors—not from the path that runs every few seconds against every worker.

#A minimal split that works behind IIS

Register a bare liveness check with no dependencies, map it at something like `/health/live`, and keep database checks on `/health/ready` with a restrictive filter (internal IP, host header, or auth) if you expose it at all. On IIS in-process hosting, both routes still run in the app pool; the win is simply not paying SQL on the hot probe path.

csharp
builder.Services.AddHealthChecks()
    .AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["live"])
    .AddSqlServer(builder.Configuration.GetConnectionString("App")!, tags: ["ready"]);

app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = c => c.Tags.Contains("live")
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = c => c.Tags.Contains("ready")
});

Point IIS Application Initialization, ARR, or your external uptime check at `/health/live` only. Run `/health/ready` from release pipelines or an internal watcher after Web Deploy, not on the five-second loop. If readiness must stay off the public internet, bind it to a management site binding or gate it in middleware—do not rely on “nobody will guess the path.”

When you investigate pool exhaustion after a publish, check probe volume first. A “healthy” dashboard that opens SQL on every tick is often the quiet source of the timeouts your real traffic hits next.