Implementing Liveness and Readiness Probes in Dropwizard
Learn how to implement custom HealthChecks in Dropwizard to distinguish between liveness and readiness probes, ensuring your service is managed correctly by orchestrators.
29 Aug 2026, 02:53 UTC

The Problem: Aggregate Health vs. Operational State
Many Dropwizard applications rely on the default /healthcheck endpoint on the admin port to tell orchestrators (like Kubernetes) if a service is running. However, the built-in registry is an aggregate: if a single non-critical background task fails, the entire endpoint returns an HTTP 500. This can trigger unnecessary container restarts (liveness failure) when the service is actually capable of handling traffic but is experiencing a transient downstream issue (readiness failure).
The goal is to implement a health check strategy that distinguishes between a process that has crashed and a process that is temporarily unable to serve requests.
Prerequisites
- A Dropwizard project (assumed version 2.x or 4.x using
io.dropwizardnamespaces). - Access to the admin connector port (default
8081). - A dependency on a managed resource, such as a database or external API, to validate.
Step 1: Creating a Custom Health Check
To monitor a specific dependency, subclass HealthCheck. The check() method must return a Result. Keep this logic lightweight; because every registered check runs on every request to the health endpoint, expensive queries can lead to a self-inflicted Denial of Service (DoS).
import com.codahale.metrics.health.HealthCheck;
import java.sql.Connection;
import java.sql.ResultSet;
import javax.sql.DataSource;
public class DatabaseHealthCheck extends HealthCheck {
private final DataSource dataSource;
public DatabaseHealthCheck(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
protected Result check() throws Exception {
try (
Connection connection = dataSource.getConnection();
ResultSet rs = connection.createStatement().executeQuery("SELECT 1")
) {
if (rs.next()) {
return Result.healthy();
}
return Result.unhealthy("Database returned no results for validation query");
} catch (Exception e) {
return Result.unhealthy("Database connection failed: " + e.getMessage());
}
}
}
Step 2: Registering Checks in the Application
Register your checks within the run method of your Application class. This attaches them to the Environment, making them visible to the admin servlet.
@Override
public void run(MyConfiguration configuration, Environment environment) {
final DataSource dataSource = // ... initialize your dataSource
// Register the database check
environment.healthChecks().register("database", new DatabaseHealthCheck(dataSource));
}
Step 3: Operational Decision: Liveness vs. Readiness
Dropwizard's default /healthcheck returns HTTP 200 only if all registered checks pass. To implement a more nuanced strategy, consider the following mapping:
| Probe Type | Purpose | Implementation Strategy | Expected Action |
|---|---|---|---|
| Liveness | Is the JVM alive? | Use the default /healthcheck but only register critical, fast checks. |
Restart Container |
| Readiness | Can it serve traffic? | Create a custom Jersey resource that checks specific dependencies or a "warm-up" flag. | Remove from Load Balancer |
Verification and Diagnostics
Run the application and use curl to verify the state of the admin port. Ensure you are targeting the admin port (8081), not the application port (8080).
Check healthy state:
curl -i http://localhost:8081/healthcheck
Expected: HTTP/1.1 200 OK and a JSON body showing "database": { "healthy": true }.
Check failure state:
Stop the database or simulate a connection failure. Run the curl command again.
Expected: HTTP/1.1 500 Internal Server Error and a JSON body containing the error message defined in your Result.unhealthy() call.
Limitations and Risks
- Admin Exposure: The
/healthcheckendpoint reveals internal dependency names and error messages. Never expose the admin port (8081) to the public internet; restrict it to your internal network or orchestrator. - Timeout Cascades: If a database check has a 30-second timeout and your orchestrator polls every 5 seconds, you can exhaust your connection pool with health check requests during a database brownout. Always set a strict, short timeout for the validation query.
- Namespace Shifts: Depending on your Dropwizard version, the
HealthCheckclass may reside incom.codahale.metrics.healthorio.dropwizard.metrics. Verify your imports against your project'spom.xmlorbuild.gradle.
Rollback
To disable a health check, remove the environment.healthChecks().register(...) line from the run method and redeploy the application. This will remove the check from the JSON output and the aggregate HTTP status calculation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.