health-go turns a Go service's dependencies into one HTTP status endpoint
Library to provide basic healthcheck functionality to Go applications.
At a glance
- What is it?
- health-go is an Apache-2.0 Go library from HelloFresh that exposes an HTTP handler reporting the health of an application, with generic checkers for ten backing services. The design decision worth understanding is SkipOnErr, which decides whether a failing dependency degrades the response or takes the whole service down.
- Who is it for?
- health-go suits Go services in a container or Kubernetes environment that want one status endpoint covering several backing services, and it suits teams that want to write their own check as a plain function alongside the built in ones. It does not suit a service that needs deeper health semantics than a pass or fail per dependency, and it does not suit a team that needs the endpoint to distinguish degraded from down without reading the response body.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two surfaces: an HTTP handler and a plain function
The library is small and does one thing. It exposes an HTTP handler that retrieves the health status of the application, and it implements generic checkers for a list of services. The usage section states that the library exports Handler and HandlerFunc functions which are fully compatible with net/http, and that it additionally exports a Measure function returning a summary status for all the registered health checks, so it can be used in non-HTTP environments. That second export is the one that gets overlooked. A service that has no HTTP surface, or one that wants to report health over a different transport, a message queue, a metrics pipeline or a sidecar, can call Measure directly rather than standing up an endpoint. The design is therefore a small registry plus two adapters, not a web framework in disguise.
Checks are named, timed functions with a skip switch
A check is a health.Config with a name, an optional timeout, a SkipOnErr flag and a Check function taking a context and returning an error. The configuration is passed at construction time through health.WithChecks and can be extended afterwards with h.Register.
h.Register(health.Config{
Name: "mysql",
Timeout: time.Second * 2,
SkipOnErr: false,
Check: healthMysql.New(healthMysql.Config{
DSN: "test:test@tcp(0.0.0.0:31726)/test?charset=utf8",
}),
})SkipOnErr is the mechanism the whole library turns on. With it set, a failing dependency is recorded but does not change the outcome, and the service reports as partially available. With it false, a failure propagates into the overall status and the endpoint answers with a 503. Timeouts are per check, and the sample uses five seconds for RabbitMQ and two for MySQL, which is a sensible pattern since a slow dependency and a slow optional dependency should not be given the same budget. The Check function is a plain closure, which means a custom dependency costs you a function and not a client library.
Ten checkers cover the usual service dependencies
The built-in checkers are listed in the README and cover RabbitMQ, PostgreSQL, Redis, HTTP, MongoDB, MySQL, gRPC, Memcached, InfluxDB and Nats. The MySQL example shows how a checker is wired, taking a DSN and returning the check function, and the same shape applies to the others. The dependency list in go.mod shows what that costs in transitive packages, which is the real trade-off of this library. The MySQL driver go-sql-driver/mysql, both pgx v4 and pgx v5 alongside the lib/pq driver, redis/go-redis, go.mongodb.org/mongo-driver, the rabbitmq amqp091-go client, nats-io/nats.go, gocql for Cassandra, the InfluxDB client, gomemcache for Memcached, and grpc all appear as direct requirements. That is a heavy import graph for a health check, and the reason it is heavy is that each checker links its client's driver into your binary. The alternative approach, which is to hand write a small ping function for each dependency, avoids those imports entirely and costs you the consistency.
The status code and the status text are separate signals
The API documentation is worth reading closely, because the response body carries more than the HTTP code does. A healthy service answers with HTTP 200 and a body holding a status of OK, a timestamp, a system block with the Go version, goroutine count, allocation counters, and a component block with the name and version you registered. A partially degraded service still answers with HTTP 200, but the body carries a status of Partially Available and a failures object naming the check that failed and its error message. A hard failure answers with 503 Service Unavailable and a status of Unavailable, again with the failures map.
{
"status": "Partially Available",
"timestamp": "2017-01-01T00:00:00.413567856+033:00",
"failures": {
"rabbitmq": "Failed during rabbitmq health check"
},
"system": {
"version": "go1.8",
"goroutines_count": 4,
"total_alloc_bytes": 21321,
"heap_objects_count": 21323,
"alloc_bytes": 234523
},
"component": {
"name": "myservice",
"version": "v1.0"
}
}The practical consequence is that an orchestrator that only looks at the HTTP status will treat a partially available service as healthy, because the code is still 200. If your platform needs the opposite behaviour, you have to parse the body, and that is a decision to make deliberately rather than discover during an incident.
HandlerFunc lets you mount the check on your own router
The second usage form exists for services that do not use the default mux. The HandlerFunc variant returns something you can attach to any router, and the example uses the chi router with a GET on the status path.
r := chi.NewRouter()
r.Get("/status", h.HandlerFunc)
http.ListenAndServe(":3000", nil)The Handler variant instead mounts directly onto net/http, which is the form the first example uses with http.Handle on the status path before ListenAndServe on port 3000. Both forms sit on top of a standard library server, so there is no server framework in the dependency tree and no opinion about your routing, middleware or TLS. That neutrality is a deliberate part of the design, and it is also what makes the library easy to drop into an existing service: you add one route and nothing else changes about how your application is served. The example server in the repository's _examples directory holds more variants, and the README points there for further cases.
Where a health endpoint stops being enough
There are clear limits to what this library models. A check is a boolean per dependency, so a service that needs to distinguish degraded from broken, or that needs a weighted score across dependencies, or that needs a check to fail only after N consecutive failures rather than on a single blip, has to build that itself, because the Config has a name, a timeout, a skip flag and a function and nothing else. A readiness and liveness split is also not part of the model, so a Kubernetes setup that wants the two probes to mean different things, one saying the process is alive and the other saying it can serve, ends up registering the same checks under two paths and hoping the skip flags differ. The alternative approach, and it is a legitimate one, is to write a dozen lines per service that pings each dependency and returns a status code you control, with no library, no client driver imports and no fixed response shape. What health-go buys is a consistent response format and a set of checkers you would otherwise write by hand, and what it costs is a dependency graph and a level of detail you cannot easily extend.
Version 5 of the module, and a quieter release cadence
The module path carries the major version, github.com/hellofresh/health-go/v5, and the go.mod requires Go 1.23.0 with a go1.23.9 toolchain. The release list shows v5.5.5 from 2025-07-07, v5.5.4 from 2025-05-16 and v5.5.3 from 2024-03-14, and the last push to the master branch was on 2026-04-17, so the code has moved since the last tagged release, and the repository is not archived. The licence is Apache-2.0, with the text in LICENSE.txt, and the project comes from HelloFresh, whose engineering blog and GitHub organisation are linked from the README. The repository layout also shows the operational tooling around the library, a Taskfile.yaml, a.golangci.yml, a docker-compose.yml, a mkdocs.yml and a docs/ directory, plus a.whitesource directory, which points to an automated dependency scanning setup. The docker-compose file is the test harness for the checkers, with services for PostgreSQL under three different drivers, RabbitMQ, Redis, MongoDB and MySQL, each with its own healthcheck block.
Editorial conclusion
health-go suits Go services in a container or Kubernetes environment that want one status endpoint covering several backing services, and it suits teams that want to write their own check as a plain function alongside the built in ones. It does not suit a service that needs deeper health semantics than a pass or fail per dependency, and it does not suit a team that needs the endpoint to distinguish degraded from down without reading the response body. Before adopting it, decide per dependency whether a failure should be a skip or a hard failure, and check the response shape your orchestrator will parse, since the status text and the HTTP code are two separate signals here.
Frequently asked questions
What does health-go provide?
It exposes an HTTP handler that retrieves the health status of the application, plus generic checkers for RabbitMQ, PostgreSQL, Redis, HTTP, MongoDB, MySQL, gRPC, Memcached, InfluxDB and Nats. It also exports a Measure function for use in non-HTTP environments.
What does SkipOnErr do in health-go?
It decides whether a failing check is recorded as a failure without changing the overall outcome, in which case the service reports as partially available, or whether the failure propagates and the endpoint answers with 503.
How do I add a health-go check to my own service?
Pass health.Config entries with health.WithChecks when calling health.New, or call h.Register afterwards. Each config carries a name, an optional timeout, a SkipOnErr flag and a Check function taking a context and returning an error.
How do I use health-go with a router other than net/http?
Use the HandlerFunc form and attach it to your own route, for example r.Get("/status", h.HandlerFunc) with the chi router. The Handler form mounts directly on net/http instead.
What licence is health-go released under?
The project is licensed under Apache-2.0, with the licence text in the LICENSE.txt file at the top of the repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hellofresh-health-go)Was this useful?