Health checks determine when a web or private service can receive traffic and whether an unhealthy process should restart. Workers and cron jobs have no HTTP listener probes; inspect their logs and task outcomes instead.
Choose TCP or HTTP
| Configuration | What passes |
|---|---|
| No health-check path | A TCP connection to the configured container port. |
A path such as /healthz | An HTTP GET returning a 2xx or 3xx response within the probe timeout. |
Bex does not substitute / when the path is empty. This lets APIs whose root
returns 404 use the TCP default. TCP proves that the listener accepts
connections, not that an application request or database query succeeds.
For a stronger signal, expose a lightweight HTTP endpoint. It should require no user login and should report whether this instance can serve requests. Avoid expensive work or long dependency waits in the handler. The same health signal also drives restarts, so consider carefully whether a shared dependency outage should make every application instance unhealthy.
Set the path
For a Git-managed service, set healthCheckPath in its Blueprint:
services:
- name: api
type: web
runtime: docker
repo: https://github.com/your-org/api
healthCheckPath: /healthzReplace the repository with yours and implement the endpoint before applying this configuration. Follow the Blueprint guide to validate and deploy it.
For the API, set BEX_API_URL, an authorized BEX_TOKEN, and SERVICE_ID:
curl --fail-with-body -X PATCH "$BEX_API_URL/v1/services/$SERVICE_ID" \
-H "Authorization: Bearer $BEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"serviceDetails":{"healthCheckPath":"/healthz"}}'To return to TCP checks, send an explicit empty string:
curl --fail-with-body -X PATCH "$BEX_API_URL/v1/services/$SERVICE_ID" \
-H "Authorization: Bearer $BEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"serviceDetails":{"healthCheckPath":""}}'JSON null is not the clear operation: the current PATCH decoder treats it
like an omitted optional value. If a previously configured / keeps failing,
clear it explicitly rather than only removing a local setting.
Understand the three probes
Bex uses the selected handler for startup, readiness, and liveness:
- Startup: allows a new instance to initialize before ordinary readiness and liveness probing take over. The current startup/rollout budget is 15 minutes.
- Readiness: removes unready instances from service traffic. A rollout waits for the new instances to become ready.
- Liveness: restarts a process after sustained health failures. The current threshold is six failing checks at a 10-second interval, approximately a minute after startup has succeeded.
The probe timeout is five seconds and the configured/default period is ten seconds. Timing is an operational budget rather than an exact wall-clock promise: scheduling and reconciliation can add delay. These are current Bex operator defaults, not per-service timing settings exposed by this guide.
A failed first deploy has no previous release to serve. Rolling updates also need sufficient capacity, and disk-backed services use a different strategy; see How deploys work.
Diagnose a failure
Check runtime logs and test the exact path on the configured listener port.
Verify the process binds 0.0.0.0, reads the injected PORT, and returns a
passing status without credentials. A 404 means the chosen path may be wrong;
a timeout can indicate slow startup, a blocked dependency, or a stuck process.
If a service is repeatedly restarting, inspect liveness failures and application errors together. Increasing replica count will not fix a consistently incorrect health endpoint. See Logging and Metrics.
Public-host /healthz during idle hibernation can be
answered by the activator with HTTP 200 before your application is reachable.
Use an application-owned readiness URL for client wake recovery. During a
rolling deploy, the same readiness endpoint should fail while the process
drains — see HTTP shutdown drain.