Is It Down For Everyone? Build a Live Website Health Checker with Quarkus
Monitor External URLs in Real-Time with MicroProfile Health, YAML Config, and a HttpClient
Keeping services available is critical, but sometimes it's not just your own microservices you care about. What if your application depends on external APIs, public endpoints, or SaaS tools? You need to know if they are down too.
In this tutorial, you'll build a simple yet powerful "Is It Down?" service with Quarkus. It checks the availability of any number of websites or APIs and reports their health through a standard liveness endpoint. This pattern is foundational for observability in microservice environments.
We’ll use:
SmallRye Health to define a custom liveness check,
YAML configuration for clean, structured site lists.
Let’s start coding.
Step 1: Project Setup
Use the Quarkus CLI to create your project with the required extensions:
quarkus create app com.example:health-checker \
--extension=quarkus-smallrye-health,quarkus-config-yaml
cd health-checker
Here’s what each extension adds:
quarkus-smallrye-health
: Enables health endpoints (/q/health
,/q/health/live
, etc.)quarkus-config-yaml
: Lets you useapplication.yaml
instead of.properties
Quarkus generates a working Maven project. You can open it in your favorite IDE and jump to the next step. Especially, if you go straight to my Github repository and clone it from there.
Step 2: Define the Monitored Sites in YAML
Let’s list the external sites we want to monitor using YAML.
Add the following to the : src/main/resources/application.yaml
monitoring:
sites:
- "https://www.google.com"
- "https://www.quarkus.io"
- "https://www.github.com"
- "http://thissitedoesnotexist.xyz"
This format is cleaner for lists and maps, which makes it ideal for configuration like ours.
Now, bind this configuration to a Java class. Rename GreetingConfig.java and change to: src/main/java/com/example/SitesConfig.java
package com.example;
import java.util.List;
import org.eclipse.microprofile.config.inject.ConfigProperties;
@ConfigProperties(prefix = "monitoring")
public class SitesConfig {
public List<String> sites;
}
The @ConfigProperties
annotation tells Quarkus to map monitoring.sites
into the sites
field automatically.
Step 3: Implement the External Health Check Logic
Now we bring everything together with a custom @Liveness
health check that:
Reads Configuration: Gets a list of websites to monitor from application.yml under monitoring.sites
Makes HTTP Requests: Sends HTTP HEAD requests to each configured external site (like Google, GitHub, etc.)
Checks Response Codes: Determines if sites are "UP" (status < 400) or "DOWN" (status ≥ 400)
Handles Failures: Catches network errors, timeouts, and other exceptions as "DOWN" status
Create: src/main/java/com/example/ExternalSitesHealthCheck.java
package com.example;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipse.microprofile.health.Liveness;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@Liveness
@ApplicationScoped
public class ExternalSitesHealthCheck implements HealthCheck {
@Inject
SitesConfig sitesConfig;
private final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
@Override
public HealthCheckResponse call() {
HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("External Websites");
boolean allSitesUp = true;
for (String siteUrl : sitesConfig.sites()) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(siteUrl))
.method("HEAD", HttpRequest.BodyPublishers.noBody())
.timeout(Duration.ofSeconds(10))
.build();
HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
if (response.statusCode() >= 400) {
responseBuilder.withData(siteUrl, "DOWN");
allSitesUp = false;
} else {
responseBuilder.withData(siteUrl, "UP");
}
} catch (Exception e) {
responseBuilder.withData(siteUrl, "DOWN - " + e.getMessage());
allSitesUp = false;
}
}
return responseBuilder.status(allSitesUp).build();
}
}
This approach ensures your /q/health/live
endpoint accurately reflects the availability of all configured external sites, while showing detailed per-site results.
Step 5: Run and Test Your Health Checker
Start the application in development mode:
quarkus dev
Open the Quarkus Health UI in your browser:
http://localhost:8080/q/health-ui
You’ll see a "External Websites" check listed. If the broken test URL is included (thissitedoesnotexist.xyz
), the overall status will be DOWN
. Remove that line from application.yaml
, save the file, and refresh the browser. Quarkus Live Reload will immediately update the health check response.
You now have a live, reactive external URL monitoring system! You can also see the raw data at: http://localhost:8080/q/health
{
"status": "DOWN",
"checks": [
{
"name": "External Websites",
"status": "DOWN",
"data": {
"https://www.google.com": "UP",
"https://www.quarkus.io": "UP",
"https://www.github.com": "UP",
"http://thissitedoesnotexist.xyz": "DOWN - null"
}
}
]
}
Where to Go From Here
You’ve just built a health checker that uses configuration-based input, efficient REST calls, and integrates with standard MicroProfile Health APIs. To take it further, consider:
Making the monitored site list reloadable without restarting the app
Adding a periodic log-based report for sites that frequently go down
Integrating with Prometheus via
quarkus-smallrye-metrics
to alert when a URL status flipsAdding a frontend that displays site statuses in real-time
For more on Quarkus Health, see: https://quarkus.io/guides/smallrye-health
This isn’t just a learning project. It’s a pattern you can immediately apply in production systems to improve reliability and response time when third-party services misbehave.
The internet is fragile. Your service doesn't have to be.
Hi Markus
the () are missing in the SitesConfig class example code