Skip to content

Add health endpoints for observability - #62

Merged
dasniko merged 3 commits into
mainfrom
feature/health-check
Sep 30, 2026
Merged

dasniko merged 3 commits into
mainfrom
feature/health-check

Conversation

@dasniko

@dasniko dasniko commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #61.

quarkus-smallrye-health exposes /q/health, /q/health/live, /q/health/ready and
/q/health/started. No hand-written HealthCheck was needed: quarkus-agroal contributes the
datasource readiness check by itself, and that is the one thing worth probing here.

What is in it

  • pom.xml — the quarkus-smallrye-health dependency.
  • HealthCheckTest — pins the two ways these endpoints break silently (see below).
  • docker-compose.yml — pg_isready on the database, curl -f .../q/health/ready on the app,
    and depends_on: condition: service_healthy in place of a bare depends_on: database.
  • CLAUDE.md / README.adoc — the endpoints and the reasoning. The README embeds
    docker-compose.yml via include::, so the healthchecks show up there automatically.

Two decisions worth reviewing

The endpoints are anonymous, on the main port. That is what lets an external uptime monitor
reach them without a Keycloak client. It also means they have to stay out of
quarkus.http.auth.permission: with quarkus.oidc.application-type=web-app, a secured probe path
answers 302 to Keycloak rather than a status, and a monitor that only checks "did I get a
response" would never notice. The test refuses to follow redirects for exactly that reason.

The external events feed is deliberately not part of readiness. EventService loads each
tenant's events JSON from a third-party URL. A probe going red on someone else's hiccup would pull
an app out of rotation that still serves every page from the 5-minute Caffeine cache. Feed failures
belong in the log.

Verification

Both test assertions were mutation-checked rather than assumed:

  • forcing quarkus.http.auth.permission onto /q/health* → Expected status code <200> but was <302>
  • -Dquarkus.datasource.health.enabled=false → JSON path checks doesn't match

pg_isready -U registration -d registration was verified against a throwaway postgres:18-alpine
container (exit=0 ... accepting connections), curl was confirmed present in
ubi9/openjdk-25-runtime:1.24 (no wget), and docker compose config -q validates.

Full suite: 49 tests, 0 failures.

An uptime monitor had nothing to poll but a page render, which costs a
database round trip, a template render and an external feed lookup just
to answer "is it up". quarkus-smallrye-health answers that directly, and
quarkus-agroal contributes the datasource readiness check by itself, so
no hand-written HealthCheck is needed.

The endpoints stay anonymous on the main port so a monitor can reach them
without a Keycloak client. That is exactly what makes them easy to break:
with oidc.application-type=web-app, a probe path pulled into
quarkus.http.auth.permission answers 302 to Keycloak instead of a status,
which a monitor checking only for "a response" would happily accept. The
test therefore refuses to follow redirects, and it asserts that readiness
carries at least one check -- disabling the datasource check would leave a
probe that reports UP while testing nothing.

The events feed is deliberately left out: it is a third-party URL, and a
readiness probe going red on someone else's hiccup would take an app out
of rotation that still serves every page from the 5-minute cache.

Signed-off-by: Niko Köbler <niko@n-k.de>
depends_on alone only waits for the container to exist, not for Postgres
to accept connections, so the app could come up first and have Flyway
fail its migration at start against a database that was not listening
yet. A pg_isready healthcheck plus condition: service_healthy closes that
race.

The app gets a healthcheck of its own against /q/health/ready, which is
what makes "unhealthy" visible in docker ps. Compose does not restart an
unhealthy container by itself -- this buys ordering and visibility, not
self-healing. curl is used because the ubi9/openjdk-25-runtime image ships
it and has no wget.

Signed-off-by: Niko Köbler <niko@n-k.de>
The two decisions that are invisible in the diff: the endpoints must stay
out of quarkus.http.auth.permission (a secured probe answers 302, not a
status), and the external events feed is intentionally not part of
readiness.

closes #61

Signed-off-by: Niko Köbler <niko@n-k.de>
@dasniko
dasniko marked this pull request as ready for review September 30, 2026 16:04
@dasniko
dasniko merged commit 1ccafdb into main Sep 30, 2026
2 checks passed
@dasniko
dasniko deleted the feature/health-check branch September 30, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add health endpoint for observability

1 participant