Still Copy-Pasting the Same Review Comments? (Sponsored)
When Reviewer A catches what Reviewer B misses, and you have 10 comments copy-pasted every sprint, it's not a people problem β it's a missing system.
Qodo finds the patterns buried in your pull request history and turns them into shared rules your whole team actually enforces. No more scattered docs, no more tribal knowledge, no more inconsistent reviews.
Deploying an application to production is only the beginning.
You need to know whether your application is healthy and whether your database, cache, and message queue are reachable.
Without proper monitoring, you will only find out about issues when your users start complaining.
I have spent years building and maintaining high-load .NET systems, and health checks have always been my first line of defense.
Today, I want to show you how to set up a production-ready monitoring system that gives you complete visibility into your application's health.
In this post, we will explore:
Why health checks matter for production applications
Adding health checks for Postgres, Redis, MongoDB, and RabbitMQ
When you run an application in a modern environment like Kubernetes or Azure, the platform needs to know if your app is ready to handle traffic.
This is where health checks come in.
A health check is a simple endpoint that returns the status of your application.
If the database or message queue is down, the health check should report that the service is "Unhealthy".
Without health checks:
Load balancers might send traffic to a broken instance.
You won't know about database connection issues until users report errors.
Background tasks might stop running without anyone noticing.
ASP.NET Core provides a built-in framework for health checks.
It allows you to check everything from database connections to external APIs and system resources.
The ShippingService uses PostgreSQL and Entity Framework Core.
We want to make sure the database is alive and that our DbContext is correctly configured.
AddNpgSql: This check connects directly to the database using the connection string. It verifies that the PostgreSQL server is live and accepting connections.
AddDbContextCheck: This check verifies that the Entity Framework Core DbContext can be created and can talk to the database. The database can be live, but your EF Core configuration can be broken. This check catches that.
To make your /health endpoint accessible, you need to map it in Program.cs:
csharp
1app.MapHealthChecks("/health");
You can customize the health check for DbContext by providing a custom test query.
In this test query, you can access any entity and perform any operation:
Both our services talk to each other via events over a message queue (RabbitMQ).
We want to make sure the message queue is alive and that our consumers are running correctly.
The first option is to install the following NuGet package:
Or, if you use MassTransit in your project, it automatically registers its own health checks into the ASP.NET Core health check system.
You don't need to add anything manually. MassTransit will report whether the message bus (e.g., RabbitMQ) is healthy and whether the consumers are running correctly.
It is registered automatically when you register MassTransit in your DI container.
When adding health checks to your project, I recommend starting by looking for a ready-made NuGet package.
If you can't find one, you can create a custom health checker.
For example, you should track the free disk space on your server.
System resources are just as important as external services.
If your server runs out of disk space, your application will crash.
We can create a custom health checker by implementing the IHealthCheck interface.
Here is a DiskSpaceHealthCheck that monitors free space on a drive:
csharp
1publicsealedclassDiskSpaceHealthCheck:IHealthCheck2{3privatereadonlystring _path;4privatereadonlylong _minFreeBytes;56publicDiskSpaceHealthCheck(string path,long minFreeBytes)7{8 _path = path;9 _minFreeBytes = minFreeBytes;10}1112publicTask<HealthCheckResult>CheckHealthAsync(13HealthCheckContext context,14CancellationToken cancellationToken =default)15{16var drive =newDriveInfo(_path);1718if(!drive.IsReady)19{20return Task.FromResult(HealthCheckResult.Unhealthy("Disk is not ready."));21}2223var free = drive.AvailableFreeSpace;2425if(free < _minFreeBytes)26{27return Task.FromResult(28 HealthCheckResult.Degraded($"Low disk space. Free bytes: {free}."));29}3031return Task.FromResult(32 HealthCheckResult.Healthy($"Disk space OK. Free bytes: {free}."));33}34}
By default, the /health endpoint returns a simple "Healthy" or "Unhealthy" string.
In a production environment, you need more details. You want to see which specific check failed and why.
We can create a HealthCheckJsonResponseWriter to return a structured JSON response:
Let's run our service and test our health endpoints:
ShippingService:
json
1{2"status":"Healthy",3"totalDuration":"00:00:00.0415370",4"entries":{5"masstransit-bus":{6"data":{7"Endpoints":{8"rabbitmq://127.0.0.1/PC_ShippingService_bus?temporary=true":{9"status":"Healthy",10"description":"ready (not started)"11}12}13},14"description":"Ready",15"duration":"00:00:00.0000459",16"status":"Healthy",17"tags":[18"ready",19"masstransit"20]21},22"database":{23"data":{2425},26"duration":"00:00:00.0413031",27"status":"Healthy",28"tags":[29"database"30]31},32"postgres":{33"data":{3435},36"duration":"00:00:00.0011592",37"status":"Healthy",38"tags":[39"database"40]41},42"disk-space":{43"data":{4445},46"description":"Disk space OK. Free bytes: 179225227264.",47"duration":"00:00:00.0001174",48"status":"Healthy",49"tags":[]50}51}52}
If your application becomes unhealthy, you want to know about it immediately.
While you can monitor the endpoint, it can be useful to check your health status in your application itself.
We can implement IHealthCheckPublisher.
This publisher runs in the background at regular intervals.
This is a simple implementation that logs changes in health reports:
Visualizing health checks makes it much easier to monitor your system.
The AspNetCore.HealthChecks.UI package provides a web dashboard that aggregates health data from all your services.
You can explore the Health Checks UI GitHub repository for more information.
1services
2.AddHealthChecksUI(options =>3{4 options.AddHealthCheckEndpoint("Shipping Service API","/health");5})6.AddInMemoryStorage();
Here, we use an in-memory storage provider for storing health reports while the application is running.
In production, depending on your needs, you may need to persist these results into the database.
Explore more persistence options for your Health Checks UI in the GitHub repository.
Note: for health-ui to be able to parse the health check responses, you need to use the UIResponseWriter.WriteHealthCheckUIResponse type.
This type comes from the AspNetCore.HealthChecks.UI.Client NuGet package.
Now you can visit /health-ui to see a professional dashboard showing the health of all your services and their dependencies:
Shippingservice:
OrderTrackingService:
Important note:
In production you should not make Health endpoint and UI visible in public.
You need to secure access to these endpoints with authentication and authorization.
You can configure this with a RequireAuthorization method:
Setting up health checks is one of the most important things you can do for your production application.
It provides visibility and enables your infrastructure to automatically respond to failures.
If you want to increase your application's visibility, I recommend adding OpenTelemetry.
Health checks show what components of your application are working correctly.
OpenTelemetry, on the other hand, provides a complete view of your application's performance, your request flows and what data is being processed.
To get started with OpenTelemetry, you can follow my article.
Hope you find this newsletter useful. See you next time.
You can download source code for this newsletter for free
Covers everything: C#, ASP.NET Core, EF Core, system design β answer each question first, reveal the solution, and a test after every chapter proves it stuck. Finish, and you earn a verifiable certificate for your LinkedIn.
Not sure where you stand? Take the free .NET Interview Run:
Find out your real level β Junior to Senior+
A realistic mock .NET interview β across 13 areas of C#, .NET, ASP.NET Core and System Design
No credit card required. When you finish, you get a personalized report: your level, your strongest and weakest areas, and where to focus next β the perfect way to benchmark yourself before diving into the Playbook.