Split Dropwizard health checks by Kubernetes probe type
Use Dropwizard HealthCheck for readiness, not liveness, and bound database probes to avoid restart loops.
05 May 2026, 18:18 UTC

The restart loop that looked like a database outage
Your Dropwizard service is running, but Kubernetes keeps killing the pod. The liveness probe receives HTTP 500 from /healthcheck because the database briefly slowed down. Kubernetes marks the container unhealthy, restarts it, and the cycle repeats. The database was never actually down long enough to matter; the health check turned a momentary blip into an outage.
The useful takeaway is simple: use Dropwizard health checks for readiness, not liveness. A readiness check verifies that dependencies such as a database are reachable before traffic arrives. A liveness check should only confirm that the local process is alive, because tying it to an external database can restart healthy pods during transient network issues.
What Dropwizard actually exposes
Dropwizard core provides the io.dropwizard.health.HealthCheck interface. During application startup, you register checks by name through the Environment object. Dropwizard aggregates the results and serves them through the admin HTTP endpoint, typically at /healthcheck, as a JSON object listing each check name and its status.
This means Kubernetes can consume the endpoint without custom parsing, but it also means every registered check contributes to the same response. If you register a database check, /healthcheck reports database status. That is exactly what you want for readiness, and exactly what you want to avoid for liveness.
A bounded database readiness check
Here is a Dropwizard 2.x example that opens a short-lived JDBC connection, runs SELECT 1, and bounds the work with a query timeout. The timeout prevents a hung database connection from consuming a pool thread for longer than the Kubernetes probe timeout.
public class DatabaseHealthCheck extends HealthCheck {
private final DataSource dataSource;
private final Duration probeTimeout;
public DatabaseHealthCheck(DataSource dataSource, Duration probeTimeout) {
this.dataSource = dataSource;
this.probeTimeout = probeTimeout;
}
@Override
protected Result check() throws Exception {
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.setQueryTimeout((int) probeTimeout.getSeconds());
statement.executeQuery("SELECT 1");
}
}
return Result.healthy("database reachable");
}
}
Register it in your application class:
environment.healthChecks()
.register("database", new DatabaseHealthCheck(dataSource, Duration.ofSeconds(2)));
The Duration.ofSeconds(2) value is a placeholder. Choose a timeout well below your Kubernetes timeoutSeconds so the probe fails fast instead of hanging.
Wiring readiness and liveness in Kubernetes
Use the aggregated admin endpoint for readiness, because it tells Kubernetes whether the database is available before the pod receives traffic:
readinessProbe:
httpGet:
path: /healthcheck
port: 8081
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
For liveness, do not point the probe at /healthcheck if that endpoint includes the database check. Use a local check instead, such as a TCP socket on the admin port or an exec probe that confirms the JVM process is responding:
livenessProbe:
tcpSocket:
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
Replace 8081 with your configured Dropwizard admin port. The admin connector is separate from the application HTTP port, so confirm the value in your Dropwizard configuration YAML before applying the manifest.
Applying the manifest changes cluster state. If the new probes cause failures, revert the manifest and reapply, or run kubectl rollout undo deployment/.
Limitations and how to verify
A health check is still load. Running it every few seconds against a small connection pool can starve the pool under traffic. Keep the query lightweight, avoid transactions, and measure response time under load before relying on it in production.
The /healthcheck response can reveal which dependencies are failing. Restrict the admin connector to internal networks or require authentication in production environments.
Dropwizard 1.x and 2.x differ in registration calls, default paths, and bundle behavior. Confirm the endpoint path and API against the version you are running.
To verify locally, start the Dropwizard application and call the admin endpoint:
curl -s http://localhost:8081/healthcheck
Expect HTTP 200 and a JSON body such as {"database":{"healthy":true}}. Then block or stop the database connection and call the endpoint again. The database check should report unhealthy while the application process continues running. If it does not, adjust the check or probe timeouts before deploying.
Health checks are a traffic signal, not a diagnosis. They tell Kubernetes when to send traffic and when to stop, but they do not replace database monitoring, connection pool metrics, or application logging.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.