Skip to main content
The devhelm.yml file defines your entire monitoring configuration. Deploy it with devhelm deploy -f devhelm.yml.

Minimal example

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):
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

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:
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:
SectionKeyReference byDocs
TagstagsnameTags & Secrets
EnvironmentsenvironmentsslugTags & Secrets
SecretssecretskeyTags & Secrets
Alert ChannelsalertChannelsnameAlert Channels
Notification PoliciesnotificationPoliciesnameNotification Policies
WebhookswebhooksurlTags & Secrets
Resource GroupsresourceGroupsnameMonitors
MonitorsmonitorsnameMonitors
Dependenciesdependenciesservice slugTags & Secrets
Status PagesstatusPagesslug

Cross-references

Resources reference each other by name (or slug), not by ID. The CLI resolves names to IDs at deploy time:
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:
alertChannels:
  - name: Slack Alerts
    config:
      channelType: slack
      webhookUrl: ${SLACK_WEBHOOK_URL}
With a default fallback:
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.
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.
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.

Multi-file configs

Pass multiple files with -f:
devhelm deploy -f base.yml -f overrides.yml --yes
Or point to a directory (all *.yml and *.yaml files are loaded in sorted order):
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

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

Monitors in YAML

All monitor types, assertions, and incident policies.

Deploy workflow

Validate, plan, and deploy lifecycle.