---
id: platform/health-checks
title: Health checks
description: Configure TCP or HTTP health checks, understand startup and restart behavior, and diagnose failed readiness.
keywords: [bex, health check, readiness probe, health check path]
last_updated: 2026-09-23
---

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:

```yaml
services:
  - name: api
    type: web
    runtime: docker
    repo: https://github.com/your-org/api
    healthCheckPath: /healthz
```

Replace the repository with yours and implement the endpoint before applying
this configuration. Follow the [Blueprint guide](./app-resource.md) to validate
and deploy it.

For the API, set `BEX_API_URL`, an authorized `BEX_TOKEN`, and `SERVICE_ID`:

```bash
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:

```bash
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](./how-deploys-work.md).

## 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](./logging.md) and [Metrics](./metrics.md).

Public-host `/healthz` during [idle hibernation](./idle-services.md) 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](./http-shutdown.md).
