Guides

Deploy with CLI

How to deploy monitors, alerts, and SLOs from yorker.config.yaml using the Yorker CLI.

Deploy with CLI

The Yorker CLI lets you define monitors, alerts, notification channels, and SLOs in a yorker.config.yaml file and deploy them with a single command. Changes are computed as a diff against the current remote state and applied in the correct order.

Install

npm install -g @yorker/cli

Authenticate

Generate an API key from Settings > API Keys in the dashboard. Export it as an environment variable:

export YORKER_API_KEY=sk_...

The CLI also accepts YORKER_API_URL to point at a different control plane (defaults to https://app.yorkermonitoring.com).

Scaffold a config

To create a starter yorker.config.yaml, run:

yorker init

The interactive wizard walks you through project name, first monitor URL, type, and frequency.

Pass flags to skip prompts:

yorker init --name my-app --url https://example.com --type http --frequency 5m

Config file structure

The yorker.config.yaml file has these top-level sections:

project: my-app              # Required. Project identifier.

alertChannels:                # Notification channel definitions.
  ops-slack:
    type: slack
    webhookUrl: "{{secrets.SLACK_WEBHOOK_URL}}"

defaults:                     # Default settings for all monitors.
  frequency: 5m
  locations:
    - loc_us_east
    - loc_eu_central

groups:                       # Groups of monitors with shared settings.
  - name: API Endpoints
    frequency: 1m
    monitors:
      - name: Users API
        type: http
        url: https://api.example.com/users

monitors:                     # Top-level monitor definitions.
  - name: Homepage
    type: http
    url: https://example.com

slos:                         # Service Level Objectives.
  - name: Homepage Availability
    monitor: Homepage
    target: "99.9%"
    window: 30d

maintenanceWindows:           # Scheduled silences / pauses.
  - name: Weekly DB maintenance
    checks: all
    mode: pause               # pause | continue
    startsAt: "2026-04-12T02:00:00Z"
    endsAt:   "2026-04-12T03:00:00Z"
    recurring: true
    recurrenceRule: "FREQ=WEEKLY;BYDAY=SU"

See the Configuration reference for the full maintenanceWindows schema.

Commands

yorker validate

Validates the config file without deploying. Checks YAML syntax, Zod schema validation, script file existence, secret interpolation, and cross-references (e.g., SLOs referencing valid monitors, alerts referencing valid channels).

yorker validate

Exit code 0 means the config is valid. Non-zero means errors were found — they are printed to stderr.

yorker diff

Shows what would change between your local config and the remote state without applying anything:

yorker diff

Output shows each resource as CREATE, UPDATE, DELETE, or UNCHANGED, with field-level diffs for updates:

Yorker deploy plan for "my-app"

  Checks:
    + CREATE  http  "API Health"  (60s, 2 locations)
    ~ UPDATE  http  "Homepage"
        ~ configJson.timeoutMs  30000 -> 15000
    = UNCHANGED  browser  "Checkout Flow"

  Summary: 1 to create, 1 to update, 1 unchanged

  (dry run — no changes applied)

yorker deploy

Applies changes to the remote state:

yorker deploy

Resources are applied in dependency order: channels first, then checks (with label sync), then alerts, SLOs, and maintenance windows. See the CLI reference for the full phase table.

Remote resources that exist but are not in your config are reported but not deleted unless you pass --prune.

yorker deploy --prune

Deletes remote resources that are not present in the config file:

yorker deploy --prune

This is useful for keeping the remote state in exact sync with the config. Prune deletions happen in the correct dependency order as part of the normal deploy phases — SLOs and alerts are removed early (phases A and B, before their parent checks in phase C), maintenance windows at phase I, and channels at phase Z. See the CLI reference for yorker deploy for the full phase table.

yorker deploy --force / --accept-remote

If someone edits a YAML-managed resource via the web UI, the next yorker deploy detects the change and aborts with a drift report. Two flags control how to resolve:

yorker deploy --force           # local config wins — overwrite remote
yorker deploy --accept-remote   # skip drifted resources — keep remote as-is

See Drift detection below for details.

yorker status

Displays the current state of all monitors:

yorker status

yorker results tail

Live-stream check results as they arrive:

yorker results tail "Homepage" --interval 30s

yorker test

Runs HTTP monitors locally against their configured URLs. Useful for validating URLs and auth before deploying:

yorker test

Browser monitors are listed but not executed locally (use Playwright directly for local browser tests).

Drift detection

The CLI tracks the state of each deployed resource in .yorker/.deploy-state.json (gitignored, per-machine). After every successful deploy, it saves a config hash and the remote updatedAt timestamp for each resource. On the next deploy, it compares:

  • Local changed? — current config hash differs from the stored hash.
  • Remote changed? — remote updatedAt is newer than the stored timestamp, and the resource has managedBy: "yaml".

This produces four possible outcomes:

Remote unchangedRemote changed
Local unchangedSkipDrift (remote-only edit)
Local changedNormal updateConflict (both sides changed)

When drift or conflicts are detected, the deploy aborts with a report showing which resources were affected. You have three options:

  1. Review and choose — inspect the remote changes in the dashboard, then update your config to match or intentionally overwrite.
  2. --force — local config wins, remote changes are overwritten.
  3. --accept-remote — drifted resources are skipped, keeping their remote state.

First deploy

On the first deploy (no state file exists), drift detection is skipped entirely — the CLI creates the state file after a successful apply.

After yorker pull

Every successful yorker pull rewrites .yorker/.deploy-state.json with a fresh snapshot of remote state, so the next deploy treats everything as a clean baseline with no drift.

.yorker/.deploy-state.json

This file is per-machine state and should not be committed. Add it to your .gitignore:

.yorker/.deploy-state.json

Secret interpolation

To keep secrets out of your config file, use placeholder syntax. The CLI resolves these at deploy time from environment variables.

{{secrets.NAME}}

Looks up YORKER_SECRET_NAME first, then falls back to NAME:

auth:
  type: bearer
  token: "{{secrets.AUTH_TOKEN}}"

Set with export YORKER_SECRET_AUTH_TOKEN=... or export AUTH_TOKEN=....

{{env.NAME}}

Looks up the environment variable directly:

monitors:
  - name: Staging API
    type: http
    url: "{{env.STAGING_URL}}/health"

${NAME}

Legacy shorthand, equivalent to {{env.NAME}}. Not applied inside browser script files to avoid conflicts with JavaScript template literals.

The CLI fails with a clear error if any placeholder is unresolved after interpolation.

CI/CD integration

Validate on push, preview changes on PRs with yorker diff, and deploy on merge. See the full CI/CD Integration guide for complete GitHub Actions and GitLab CI workflows.

# GitHub Actions — quick start
- run: npm install -g @yorker/cli
- run: yorker validate
- run: yorker diff
- run: yorker deploy --force                    # CI owns the config
  if: github.ref == 'refs/heads/main'

Use --force in CI pipelines where the config file is the source of truth. If you want CI to preserve manual edits made via the dashboard, use --accept-remote instead.

Set YORKER_API_KEY and any YORKER_SECRET_* variables at the job or workflow level so all steps — including validate — can resolve {{secrets.*}} placeholders.

Complete example

project: my-app

alertChannels:
  ops-slack:
    type: slack
    webhookUrl: "{{secrets.SLACK_WEBHOOK_URL}}"
  on-call-email:
    type: email
    addresses:
      - [email protected]
  pagerduty:
    type: webhook
    url: "{{secrets.PAGERDUTY_WEBHOOK_URL}}"
    method: POST
    headers:
      Authorization: "Token token={{secrets.PD_TOKEN}}"

defaults:
  frequency: 5m
  locations:
    - loc_us_east
    - loc_eu_central
  http:
    timeoutMs: 15000
    followRedirects: true
    assertions:
      - type: status_code
        value: 200
  browser:
    viewport:
      width: 1280
      height: 720
    screenshotMode: every_step
  alerts:
    - conditions:
        - type: consecutive_failures
          count: 2
      channels:
        - "@ops-slack"

groups:
  - name: Critical APIs
    frequency: 1m
    locations:
      - loc_us_east
      - loc_us_west
      - loc_eu_central
    alerts:
      - conditions:
          - type: consecutive_failures
            count: 1
          - type: multi_location_failure
            minLocations: 2
        channels:
          - "@ops-slack"
          - "@pagerduty"
    monitors:
      - name: Payments API
        type: http
        url: https://api.example.com/payments
        assertions:
          - type: status_code
            value: 200
          - type: response_time
            max: 1000
      - name: Auth API
        type: http
        url: https://api.example.com/auth/health

monitors:
  - name: Homepage
    type: http
    url: https://example.com
  - name: Checkout Flow
    type: browser
    script: ./monitors/checkout.ts
    frequency: 5m
    alerts:
      - name: checkout-warning
        conditions:
          - type: consecutive_failures
            count: 2
        channels:
          - "@ops-slack"
      - name: checkout-critical
        conditions:
          - type: consecutive_failures
            count: 5
        channels:
          - "@pagerduty"
          - "@on-call-email"

slos:
  - name: Homepage Availability
    monitor: Homepage
    target: "99.95%"
    window: 30d
    channels:
      - "@ops-slack"
  - name: Checkout Availability
    monitor: Checkout Flow
    target: "99.9%"
    window: 30d
    channels:
      - "@ops-slack"
      - "@pagerduty"