> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devhelm.io/llms.txt
> Use this file to discover all available pages before exploring further.

# YAML File Format

> DevHelm YAML configuration schema — version field, resource sections, and file structure

The `devhelm.yml` file defines your entire monitoring configuration. Deploy it with `devhelm deploy -f devhelm.yml`.

## Minimal example

```yaml theme={null}
version: "1"

monitors:
  - name: API Health
    type: HTTP
    config:
      url: https://api.example.com/health
      method: GET
    frequencySeconds: 60
    regions: [us-east]
```

## Top-level structure

The file accepts `version`, `defaults`, `moved`, and ten resource sections. All section keys are **camelCase**. Every section is optional — include only what you need (but the file must contain at least one resource section):

```yaml theme={null}
version: "1"

defaults:             # default values applied to all monitors
  monitors:
    frequencySeconds: 60
    enabled: true
    regions: [us-east, eu-west]

tags: []              # organization-wide labels
environments: []      # deployment stages with variables
secrets: []           # vault secrets (write-only)
alertChannels: []     # where notifications are sent
notificationPolicies: []  # escalation chains and match rules
webhooks: []          # platform event webhooks
resourceGroups: []    # composite health groups
monitors: []          # the monitoring checks themselves
dependencies: []      # third-party service subscriptions
statusPages: []       # public status pages
```

The schema is strict: unknown top-level keys (and unknown keys inside any resource) are rejected at validate time.

## version

```yaml theme={null}
version: "1"

monitors:
  - name: API Health
    type: HTTP
    config:
      url: https://api.example.com/health
      method: GET
```

The schema version. Currently `"1"` is the only supported value. The CLI warns on unrecognized versions but does not reject them. A config consisting of only `version` (no resource sections) is rejected with "Config has no resource definitions".

## defaults

Set default values that apply to all monitors unless overridden:

```yaml theme={null}
defaults:
  monitors:
    frequencySeconds: 60
    enabled: true
    regions: [us-east, eu-west]
    alertChannels: [Slack Alerts]
    incidentPolicy:
      triggerRules:
        - type: consecutive_failures
          count: 3
          scope: per_region
          severity: down
      confirmation:
        type: multi_region
        minRegionsFailing: 2
      recovery:
        consecutiveSuccesses: 2

monitors:
  - name: API Health
    type: HTTP
    config:
      url: https://api.example.com/health
      method: GET
```

Defaults are applied with **shallow** per-field merge: if a monitor sets a field, the monitor value wins. Nested objects like `incidentPolicy` are replaced entirely, not deep-merged. A default `incidentPolicy` must be complete — `triggerRules`, `confirmation`, and `recovery` are all required whenever the block is present.

## Resource sections

Each section is detailed on its own page:

| Section               | Key                    | Reference by   | Docs                                                     |
| --------------------- | ---------------------- | -------------- | -------------------------------------------------------- |
| Tags                  | `tags`                 | `name`         | [Tags & Secrets](/mac/yaml/tags-and-secrets)             |
| Environments          | `environments`         | `slug`         | [Tags & Secrets](/mac/yaml/tags-and-secrets)             |
| Secrets               | `secrets`              | `key`          | [Tags & Secrets](/mac/yaml/tags-and-secrets)             |
| Alert Channels        | `alertChannels`        | `name`         | [Alert Channels](/mac/yaml/alert-channels)               |
| Notification Policies | `notificationPolicies` | `name`         | [Notification Policies](/mac/yaml/notification-policies) |
| Webhooks              | `webhooks`             | `url`          | [Tags & Secrets](/mac/yaml/tags-and-secrets)             |
| Resource Groups       | `resourceGroups`       | `name`         | [Monitors](/mac/yaml/monitors)                           |
| Monitors              | `monitors`             | `name`         | [Monitors](/mac/yaml/monitors)                           |
| Dependencies          | `dependencies`         | `service` slug | [Tags & Secrets](/mac/yaml/tags-and-secrets)             |
| Status Pages          | `statusPages`          | `slug`         | —                                                        |

### Cross-references

Resources reference each other by **name** (or slug), not by ID. The CLI resolves names to IDs at deploy time:

```yaml theme={null}
monitors:
  - name: API Health
    type: HTTP
    config:
      url: https://api.example.com/health
      method: GET
    tags: [production]            # references tag by name
    alertChannels: [Slack Alerts] # references alert channel by name
    environment: staging          # references environment by slug
    auth:
      type: bearer                # bearer | basic | api_key | header
      secret: API_TOKEN           # references secret by key
```

## Environment variable interpolation

Use `${VAR}` syntax to inject environment variables into any string value:

```yaml theme={null}
alertChannels:
  - name: Slack Alerts
    config:
      channelType: slack
      webhookUrl: ${SLACK_WEBHOOK_URL}
```

With a default fallback:

```yaml theme={null}
monitors:
  - name: API Health
    type: HTTP
    config:
      url: ${API_URL:-https://api.example.com/health}
      method: GET
```

Environment variables are resolved **after** YAML parsing, inside string values only — values containing YAML metacharacters can never alter document structure. If a required variable is missing (or set to the empty string), the CLI exits with an error listing all unresolved variables. Use `$$` for a literal `$`, and `${VAR:-}` to explicitly allow an empty value.

<Note>
  Because interpolation happens inside string values, `${VAR}` only works in **string** fields (URLs, tokens, names). Numeric fields like `frequencySeconds` cannot be interpolated — `frequencySeconds: ${FREQ:-60}` fails validation with "Expected number, received string". Vary numeric fields across environments with separate files instead.
</Note>

<Note>
  Environment variable interpolation (`${VAR}`) is different from vault secrets. Variables are resolved from the shell environment at deploy time. Vault secrets are stored in DevHelm and referenced by key in `auth` blocks.
</Note>

## Multi-file configs

Pass multiple files with `-f`:

```bash theme={null}
devhelm deploy -f base.yml -f overrides.yml --yes
```

Or point to a directory (all `*.yml` and `*.yaml` files are loaded in sorted order):

```bash theme={null}
devhelm deploy -f config/ --yes
```

Sections from all files are **concatenated**. Duplicate names within a resource type are a validation error ("names must be unique within each resource type") — files must define disjoint resources; there is no override/last-wins merging. The `defaults` block is the one exception: later files' `defaults.monitors` fields shallow-merge over earlier ones.

## Full example

```yaml theme={null}
version: "1"

defaults:
  monitors:
    frequencySeconds: 60
    regions: [us-east, eu-west]

tags:
  - name: production
    color: "#10b981"
  - name: api
    color: "#3b82f6"

environments:
  - name: Production
    slug: production
    variables:
      BASE_URL: https://api.example.com

secrets:
  - key: SLACK_WEBHOOK_URL
    value: ${SLACK_WEBHOOK_URL}

alertChannels:
  - name: Slack Alerts
    config:
      channelType: slack
      webhookUrl: ${SLACK_WEBHOOK_URL}

  - name: PagerDuty On-Call
    config:
      channelType: pagerduty
      routingKey: ${PAGERDUTY_ROUTING_KEY}

notificationPolicies:
  - name: Critical Alerts
    enabled: true
    priority: 10
    matchRules:
      - type: monitor_tag_in
        values: [production]
    escalation:
      steps:
        - channels: [Slack Alerts]
          delayMinutes: 0
        - channels: [PagerDuty On-Call]
          delayMinutes: 5
          requireAck: true

monitors:
  - name: API Health
    type: HTTP
    config:
      url: https://api.example.com/health
      method: GET
    tags: [production, api]
    alertChannels: [Slack Alerts]
    assertions:
      - config:
          type: status_code
          expected: "200"
          operator: equals
        severity: fail

dependencies:
  - service: github
    alertSensitivity: INCIDENTS_ONLY
```

## Next steps

<CardGroup cols={2}>
  <Card title="Monitors in YAML" icon="signal" href="/mac/yaml/monitors">
    All monitor types, assertions, and incident policies.
  </Card>

  <Card title="Deploy workflow" icon="rocket" href="/mac/yaml/workflow">
    Validate, plan, and deploy lifecycle.
  </Card>
</CardGroup>
