-
Notifications
You must be signed in to change notification settings - Fork 164
add: Health endpoint documentation #613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Kump3r
wants to merge
3
commits into
concourse:master
Choose a base branch
from
Kump3r:health-endpoint-documentation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,300 @@ | ||
| --- | ||
| title: Health Endpoint | ||
| --- | ||
|
|
||
| ## `GET /api/v1/health` | ||
|
|
||
| The health endpoint is an HTTP endpoint exposed by the [`web` node](../install/running-web.md) that reports whether | ||
| the Concourse deployment is in a healthy state. Operators and monitoring tools can poll it to get a fast, structured | ||
| answer about the state of the system — without needing to log in or use `fly`. | ||
|
|
||
| The endpoint requires no authentication and returns a JSON body along with an HTTP status code that reflects the | ||
| current health state. | ||
|
|
||
| ## Health states | ||
|
|
||
| The endpoint reports one of three states: | ||
|
|
||
| **Ok** — everything is working as expected. The number of connected workers meets the configured minimum, and | ||
| all internal components are keeping up with their scheduled work. | ||
|
|
||
| **Degraded** — the system is running but something needs attention. Either the number of connected workers has | ||
| dropped below [`--health-min-worker-count`](#--health-min-worker-count), or one or more internal components (such as | ||
| the build scheduler, resource checker, or build tracker) have fallen behind and are considered stale. Builds can | ||
| still run. This is a warning state — the system is operational, but the operator should investigate. | ||
|
|
||
| **Failing** — the system is in a critical state. Either the database is unreachable or read-only, or there are no | ||
| connected workers. Builds cannot run. | ||
|
|
||
| ## Response format | ||
|
|
||
| The response body is a JSON object with the following top-level fields: | ||
|
|
||
| | Field | Description | | ||
| |---|---| | ||
| | `status` | One of `"ok"`, `"degraded"`, or `"failing"`. | | ||
| | `timestamp` | RFC 3339 timestamp of when the response was generated. | | ||
| | `database` | Database connectivity status. See below for details. | | ||
| | `workers` | Connected worker counts. See below for details. | | ||
| | `components` | Array of internal background components and their health. See below for details. | | ||
|
|
||
| **`database` fields:** | ||
|
|
||
| | Field | Description | | ||
| |---|---| | ||
| | `status` | `"healthy"` or `"unhealthy"`. | | ||
| | `error` | Human-readable error message. Only present when `status` is `"unhealthy"`. | | ||
|
|
||
| **`workers` fields:** | ||
|
|
||
| | Field | Description | | ||
| |---|---| | ||
| | `status` | One of `"healthy"`, `"degraded"`, or `"unhealthy"`. | | ||
| | `total` | Total number of registered workers. | | ||
| | `running` | Number of workers currently in the `running` state. | | ||
| | `unhealthy_workers` | Array of the names of workers not in the `running` state. Not present if all workers are healthy. | | ||
|
|
||
| **`components` fields (one object per component):** | ||
|
|
||
| | Field | Description | | ||
| |---|---| | ||
| | `name` | Component identifier (e.g. `"scheduler"`, `"tracker"`, `"collector_volumes"`) | | ||
| | `status` | `"healthy"` if the component is keeping up with its scheduled work, `"unhealthy"` if stale. | | ||
| | `paused` | `true` if the component has been administratively paused. | | ||
| | `last_ran` | RFC 3339 timestamp of the last completed run. | | ||
|
|
||
| ### Ok | ||
|
|
||
| ``` | ||
| HTTP 200 OK | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "status": "ok", | ||
| "timestamp": "2026-06-08T10:05:00Z", | ||
| "database": { "status": "healthy" }, | ||
| "workers": { "status": "healthy", "total": 3, "running": 3 }, | ||
| "components": [ | ||
| { "name": "scheduler", "status": "healthy", "paused": false, "last_ran": "2026-06-08T10:05:00Z" }, | ||
| { "name": "tracker", "status": "healthy", "paused": false, "last_ran": "2026-06-08T10:05:00Z" }, | ||
| { "name": "collector_volumes", "status": "healthy", "paused": false, "last_ran": "2026-06-08T10:05:00Z" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ### Degraded | ||
|
|
||
| ``` | ||
| HTTP 200 OK | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "status": "degraded", | ||
| "timestamp": "2026-06-08T10:05:00Z", | ||
| "database": { "status": "healthy" }, | ||
| "workers": { "status": "healthy", "total": 3, "running": 3 }, | ||
| "components": [ | ||
| { "name": "scheduler", "status": "unhealthy", "paused": false, "last_ran": "2026-06-08T10:00:00Z" }, | ||
| { "name": "tracker", "status": "healthy", "paused": false, "last_ran": "2026-06-08T10:05:00Z" }, | ||
| { "name": "collector_volumes", "status": "unhealthy", "paused": false, "last_ran": "2026-06-07T08:00:00Z" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| In this example the `scheduler` component is stale, which drives the top-level | ||
| `degraded` status. `collector_volumes` is also stale, but because it is not a | ||
| runtime-critical component, it does not contribute to the top-level status on | ||
| its own. | ||
|
|
||
| ### Failing | ||
|
|
||
| ``` | ||
| HTTP 503 Service Unavailable | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "status": "failing", | ||
| "timestamp": "2026-06-08T10:05:00Z", | ||
| "database": { "status": "unhealthy", "error": "database unreachable" }, | ||
| "workers": { "status": "unhealthy", "total": 0, "running": 0 }, | ||
| "components": [ | ||
| { "name": "scheduler", "status": "unhealthy", "paused": false, "last_ran": "2026-06-08T10:00:00Z" }, | ||
| { "name": "tracker", "status": "unhealthy", "paused": false, "last_ran": "2026-06-08T10:00:00Z" }, | ||
| { "name": "collector_volumes", "status": "unhealthy", "paused": false, "last_ran": "2026-06-07T08:00:00Z" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ## What makes a component stale? | ||
|
|
||
| Every component runs on a fixed interval. If a component hasn't completed a run for longer than its interval | ||
| multiplied by `--health-component-stale-multiplier`, it is marked stale (`"status": "unhealthy"`) in the | ||
| `components` array. | ||
|
|
||
| For example: if the build scheduler runs every 10 seconds and the stale multiplier is `2.0`, the scheduler is | ||
| considered stale after 20 seconds without a completed run. | ||
|
|
||
| A stale component usually means the `web` node is under heavy load or is stuck. Check the `web` node logs for errors | ||
| if a component stays stale. | ||
|
|
||
| ### Which components affect the top-level status? | ||
|
|
||
| Not all stale components drive the top-level `status` field. Only three are | ||
| considered critical for scheduling and running builds: | ||
|
|
||
| | Component | Description | | ||
| |---|---| | ||
| | `scheduler` | Determines when new job builds should run. See [Build Scheduler](../internals/scheduler.md). | | ||
| | `tracker` | Picks up and executes started builds. See [Build Tracker](../internals/build-tracker.md). | | ||
| | `scanner` | Schedules resource checks. See [Resource Checker](../internals/checker.md). | | ||
|
|
||
| If any of these three fall behind, the top-level `status` becomes `degraded` (or `failing` if workers are also gone). | ||
|
|
||
| All other components — the garbage collection collectors, `pipeline_pauser`, `being_watched_build_marker`, | ||
| `signing_key_lifecycler`, and others — appear in the `components` array with their own `status` field, | ||
| but a stale value for any of them does not change the top-level `status`. They are worth monitoring, but a stale GC | ||
| collector does not by itself indicate that builds are at risk of not running. | ||
|
|
||
| ## Web Configuration flags | ||
|
|
||
| The following flags can be set on the `concourse web` command to change the behaviour of the health endpoint. | ||
|
|
||
| ### `--health-min-worker-count` | ||
|
|
||
| Controls how many connected workers are required for the endpoint to report `ok`. When the number of connected | ||
| workers in the `running` state drops below this value, the endpoint reports `degraded`. | ||
|
|
||
| Setting this to `0` disables the worker count check entirely — the endpoint will never report `degraded` due to | ||
| worker count, and will only transition to `failing` when there are no running workers at all. | ||
|
|
||
| | | | | ||
| |---|---| | ||
| | **Flag** | `--health-min-worker-count` | | ||
| | **Env var** | `CONCOURSE_HEALTH_MIN_WORKER_COUNT` | | ||
| | **Type** | integer | | ||
| | **Default** | `1` | | ||
|
|
||
| ```properties | ||
| CONCOURSE_HEALTH_MIN_WORKER_COUNT=2 | ||
| ``` | ||
|
|
||
| ### `--health-component-stale-multiplier` | ||
|
|
||
| Controls how tolerant the endpoint is of slow or delayed background components. A component is considered stale when | ||
| it hasn't run for longer than its interval multiplied by this value. | ||
|
|
||
| Increasing the multiplier gives components more time before they are marked stale, which reduces false positives in | ||
| environments where the `web` node is occasionally slow. Decreasing it makes the endpoint more sensitive — useful if | ||
| you want earlier warnings when a component falls behind. | ||
|
|
||
| | | | | ||
| |---|---| | ||
| | **Flag** | `--health-component-stale-multiplier` | | ||
| | **Env var** | `CONCOURSE_HEALTH_COMPONENT_STALE_MULTIPLIER` | | ||
| | **Type** | float | | ||
| | **Default** | `2.0` | | ||
|
|
||
| ```properties | ||
| CONCOURSE_HEALTH_COMPONENT_STALE_MULTIPLIER=3.0 | ||
| ``` | ||
|
|
||
| ## Related `fly` commands | ||
|
|
||
| ### `fly workers` | ||
|
|
||
| To see the number of connected workers and their current state, run: | ||
|
|
||
| ```shell | ||
| fly -t example workers | ||
| ``` | ||
|
|
||
| This can help explain why the health endpoint is reporting `degraded` or `failing`. A worker in the `stalled` or | ||
| `landing` state does not count toward the `workers.running` total. Only workers in the `running` state are counted. | ||
|
|
||
| ``` | ||
| name platform tags team state version | ||
| worker-1 linux none none running 2.3 | ||
| worker-2 linux none none stalled 2.3 | ||
| ``` | ||
|
|
||
| In this example, `workers.running` would be `1`. If `--health-min-worker-count` is `2`, the health endpoint would | ||
| report `degraded`. | ||
|
|
||
| See [Administration](administration.md#fly-workers) for more on managing workers. | ||
|
|
||
| ### `fly health` | ||
|
|
||
| To check the health of a Concourse deployment, run: | ||
|
|
||
| ```shell | ||
| fly -t example health | ||
| ``` | ||
|
|
||
| This prints a summary table with one row per subsystem and component: | ||
|
|
||
| ``` | ||
| subsystem status detail | ||
| overall ok 2026-06-08T10:05:00Z | ||
| database healthy | ||
| workers healthy 3/3 running | ||
| scheduler healthy last ran: 2026-06-08T10:04:50Z | ||
| tracker healthy last ran: 2026-06-08T10:04:55Z | ||
| scanner healthy last ran: 2026-06-08T10:04:45Z | ||
| collector_volumes unhealthy last ran: 2026-06-07T08:00:00Z | ||
| pipeline_pauser healthy paused | ||
| ``` | ||
|
|
||
| The `overall` row shows the top-level status and the timestamp from the response. The `workers` row shows how many | ||
| workers are running out of how many are registered; if any are not running, their names appear in the detail column. | ||
| Each component row shows when it last ran, or `paused` if the component has been administratively paused. | ||
|
|
||
| Status values are color-coded: green for `ok` / `healthy`, yellow for `degraded`, red for `failing` / `unhealthy`. | ||
|
|
||
| !!! note | ||
|
|
||
| The exit code is always `0`, even when the cluster is failing. `fly health` is informational, consistent with | ||
| `fly status`. | ||
|
|
||
| To get the raw JSON response instead — useful for scripting or piping to `jq`: | ||
|
|
||
| ```shell | ||
| fly -t example health --json | ||
| ``` | ||
|
|
||
| `fly h` is a supported alias of `fly health`. | ||
|
|
||
| ## Usage as a monitoring probe | ||
|
|
||
| The health endpoint is well suited for use as an external monitoring probe or a Kubernetes health check. | ||
|
|
||
| **Kubernetes liveness and readiness probes:** | ||
|
|
||
| ```yaml | ||
| livenessProbe: | ||
| httpGet: | ||
| path: /api/v1/health | ||
| port: 8080 | ||
| initialDelaySeconds: 10 | ||
| periodSeconds: 15 | ||
|
|
||
| readinessProbe: | ||
| httpGet: | ||
| path: /api/v1/health | ||
| port: 8080 | ||
| initialDelaySeconds: 10 | ||
| periodSeconds: 15 | ||
| ``` | ||
|
|
||
| Note that both `ok` and `degraded` return HTTP `200`, so both probes will pass in either state. Only `failing` | ||
| returns HTTP `503`, which will cause the probe to fail. If you want readiness to fail while the system is degraded, | ||
| you will need a wrapper script or a custom probe that inspects the `status` field in the response body. | ||
|
|
||
| **Alerting pipelines:** | ||
|
|
||
| If you have an external monitor (such as Prometheus Blackbox Exporter, Nagios, or a Concourse pipeline itself), | ||
| pointing it at `/api/v1/health` and alerting on anything other than `HTTP 200` gives you a fast signal for failing | ||
| states. The `components` field in the response body provides the detail needed to start investigating alerts easily. | ||
| For example, if the `scheduler` is stale, you can check the `web` node logs for errors. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was reviewing concourse/concourse#9640 and noticed the client code didn't handle an HTTP 333 response. I then realized I don't recall this status code being a thing in the original PR. I looked at the health endpoint handler and I'm not seeing where we return
HTTP 333: https://github.com/concourse/concourse/blob/master/atc/api/healthserver/health.goI don't think HTTP status codes were discussed in the RFC as well, which is fine. This section here should be updated to reflect the code OR the code should be updated to reflect the docs here. I'm a bit worried about returning a non-standard HTTP status code, so updating the docs to reflect the fact that we actually return HTTP 200 here is probably what we should do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I just forgot to fix this I guess. I had some stuff noted down during testing different approaches and reading trough articles and pushed an older version of the docs, not the final one. Silly me, good catch. There were also other things that were not from the latest
healthservice merged. I will commit the most recent version. Sorry for the noise.