This page covers how to write health checks with Knock, attach diagnostic data, understand aggregation, and read the resulting payload. Every example compiles against the MicroProfile Health 4.0 API.
Probe qualifiers
Each check declares exactly one probe qualifier. The qualifier selects which endpoint reports it.
| Qualifier | Endpoint | Meaning |
|---|---|---|
|
|
The instance is running and not in an unrecoverable state. A |
|
|
The instance is ready to receive traffic (dependencies reachable, caches warm). |
|
|
Slow initialisation has completed. Lets orchestrators delay liveness probing. |
GET /health aggregates all registered checks regardless of qualifier.
|
A |
Writing a check
@Readiness
@ApplicationScoped
public class BrokerCheck implements HealthCheck {
@Inject MessageBroker broker;
@Override
public HealthCheckResponse call() {
return broker.isConnected()
? HealthCheckResponse.up("message-broker")
: HealthCheckResponse.down("message-broker");
}
}
Attaching data
The response builder accepts typed withData(…) entries. Knock serialises String, long, boolean and int natively; any other type is rendered with String.valueOf(…).
return HealthCheckResponse.named("disk-space")
.status(freeBytes > threshold)
.withData("free", freeBytes) // long -> JSON number
.withData("path", "/var/lib") // String -> JSON string
.withData("writable", true) // boolean -> JSON boolean
.build();
{
"name": "disk-space",
"status": "UP",
"data": { "free": 5368709120, "path": "/var/lib", "writable": true }
}
The data object is omitted entirely when no entry is added.
Aggregation rules
The aggregate status of a probe group follows MicroProfile Health §3.2:
-
the global status is DOWN as soon as one check reports
DOWN; -
an empty check list yields UP (the probe is considered healthy);
-
an exception thrown by
HealthCheck.call()is converted to aDOWNcheck whosedatacarries the exception class name — it never propagates out of the endpoint.
{
"status": "DOWN",
"checks": [
{ "name": "database", "status": "UP" },
{ "name": "broker", "status": "DOWN",
"data": { "error": "java.net.ConnectException" } }
]
}
Execution model
Checks within a probe group are executed on a VirtualThreadPerTaskExecutor: each call() runs on its own virtual thread, so a slow check never blocks the others. No platform-thread pool is allocated and no ThreadLocal is used.
Standalone (Java SE)
Without CDI, register checks directly and ask the facade for a report:
HealthCheckRegistry registry = new KnockHealthCheckRegistry();
registry.register(new DatabaseCheck());
registry.register(new BrokerCheck());
KnockHealthService service = new KnockHealthService(registry);
HealthReport live = service.report(ProbeType.LIVENESS);
HealthReport all = service.report(ProbeType.ALL);
HealthReport exposes httpStatus() (200 / 503) and json() (the serialised payload).
With CDI (Vauban)
Add knock-cdi-vauban and Knock discovers every @Liveness/@Readiness/@Startup bean automatically: the Build Compatible Extension validates them, and HealthCheckRegistrar registers them when the application context is initialised. No manual registration is required.