---
title: "Watch your app's health and resource pressure"
description: "Add health probes, a dependency check, SLO targets and resource limits, then read the state from the terminal and from code."
section: "guides"
status: "Building for 0.4"
spec: "health"
canonical: "https://mainsdk.dev/docs/guides/alerts-and-health"
updated: "2026-10-28"
---

# Watch your app's health and resource pressure

> Add health probes, a dependency check, SLO targets and resource limits, then read the state from the terminal and from code.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/alerts-and-health · Updated: 2026-10-28

You will make an app report when it is ready, when a dependency is down and when it is short of CPU or memory, and read that state from the terminal and from code. It takes about 15 minutes. Alerts that reach people (spec `alerts`) are planned and not covered here: today a burn or a state change is logged with a code and exported as a metric.

## You need

- A MainSDK app with a `main.yaml`, run from the repository (nothing here is published to npm).
- One dependency you can ping, such as your database or a partner API.

## 1. Declare the sections

```yaml title="main.yaml"
health:
  critical: [payments]
slo:
  availability: 99.9%
resources:
  pressure: { loop: 100ms, memory: 80% }
  shed: { loop: 500ms }
  critical: ["POST /api/checkout/**"]
```

`payments` is the check you add next. `critical` routes keep running when the instance sheds load.

## 2. Register a check

```ts title="app/status.ts"
import { status } from "mainsdk";

const pingProvider = async () => ({ ok: true });   // replace with a real ping of your provider
status.check("payments", async () => (await pingProvider()).ok, { critical: true });
```

`pingProvider` stands in for your own function. The check runs every `health.every` (10 seconds) in the background. You should see `payments` in the dependency list once the app starts.

## 3. Read the probes

Your server adapter answers `/healthz` and `/readyz` for you. To read the same answer in code:

```ts title="app/ready.ts"
import { app } from "mainsdk";

const res = app().health.handle(new Request("http://localhost/readyz"));
console.log(res?.status, await res?.text());
```

You should see `200` and a body with `"status":"ready"`. While `payments` is down it is `503` with `MAIN-HEALTH-0001`.

## 4. React to pressure

```ts title="app/cache.ts"
import { resources } from "mainsdk";

const localCache = new Map<string, string>();   // the app's own in-memory cache
resources.onPressure(() => localCache.clear());
```

`cache` is your own. Under pressure, job attempts and workflow runs wait (except those in `resources.critical`), and `/readyz` says `degraded`. Past the `shed` thresholds new non-critical requests get 503 with `Retry-After`.

## 5. Look from the terminal

```bash
npx mainsdk status
```

You should see one line per dependency and a `resources` line with the state. Add `--json` for a script.

## Verify

```bash
npx mainsdk check
```

A pass prints no `MAIN-HEALTH` or `MAIN-RESOURCE` findings. A value outside the allowed range is reported before you deploy.

## If it fails

| Code | Cause | Fix |
| --- | --- | --- |
| `MAIN-HEALTH-0005` | `slo.availability` outside 90% to 99.999%, or a latency that is not a duration | Use a value such as `99.9%` and `300ms` |
| `MAIN-HEALTH-0006` | `health.critical` names `mainsdk-cloud`, `tuf` or something the app does not configure, or `drain-delay` is too close to `deadline` | Name a driver section or a `status.check`; lower `drain-delay` |
| `MAIN-RESOURCE-0003` | A `resources` value is invalid, for example a `shed` threshold not above its `pressure` threshold | Fix the value the finding names |
| `MAIN-HEALTH-0004` | A probe threw or timed out | Fix the dependency, or raise `health.timeout` |
| `MAIN-RESOURCE-0001` | A request was shed under load | Add its route to `resources.critical`, or reduce load |

## For agents

```text
Goal: add health probes, one dependency check and resource limits to this app.
Touch: main.yaml (health, slo, resources sections) and one file that calls status.check and resources.onPressure.
Do not: list mainsdk-cloud or tuf under health.critical, or loosen a threshold to make a check pass.
Verify: npx mainsdk check, then npx mainsdk status.
Stop and ask if: a critical route is unknown, or a dependency has no cheap way to ping it.
```

## Next

- [Health and status page](https://mainsdk.dev/docs/interfaces/status-page)
- [Fail static](https://mainsdk.dev/docs/concepts/fail-static)
- [See every call, decision and change your app makes](https://mainsdk.dev/docs/guides/see-what-your-app-does)
