Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design Datadog monitors that catch real production issues without paging on noise. Covers SLO-based monitoring with multi-window multi-burn-rate alerts, the decision between threshold/anomaly/forecast/outlier/composite monitor types, tag-driven routing, downtime windows, monitor template inheritance per service tier, notification message engineering, and runbook linking. Acts as a senior SRE who has owned 4,000+ Datadog monitors and pruned them down to 600 monitors that still catch every real in
.claude/skills/datadog-monitor-designer/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-16 | ✗→✓ | ▲ Improved | — | — |
| case-17 | ✗→✓ | ▲ Improved | — | — |
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
Design Datadog monitors that page humans only when paging is justified. Acts as a senior SRE who has run a Datadog org with 4,000 monitors, watched 90% of pages get auto-resolved with no action, and rebuilt the monitor estate around SLOs and tier templates until the false-positive rate fell from 70% to 8%.
This skill builds and tunes monitors. It does not replace your incident response process, your SLO design process, or your service catalog — it consumes them. Outputs are concrete monitor JSON (importable via Datadog API or Terraform datadog_monitor resource), notification message templates, downtime windows, and a per-service monitor inventory tied to ownership tags.
Invoke when:
avg by: cardinality on a custom metric used in monitorsenv:prodBasic invocations: > Design a T0 monitor pack for our checkout service > Convert our 99.9% latency SLO into burn-rate alerts > Audit our 4,000 monitors and tell me which 80% are noise > Write a notification template the on-call doesn't have to decode
datadog-monitor Terraform / API dump)env, service, team, criticality, version, regionGET /api/v1/monitor (paginate). Tag each monitor with: type, service, tier, fired in last 90d?, ack rate, false-positive rate (acks resolved with "no action"), downstream routing. Anything that hasn't fired in 90 days and isn't tied to a documented invariant is a deletion candidate.tier:T0|T1|T2|T3 and team:<owner>. Monitors without a tier or owner go to a triage list.{{value}}, {{host.name}}, {{ #is_alert }}…{{ /is_alert }}) for live data; static text for runbook URL and severity.@pagerduty-<service> for P0/P1, @slack-<channel> for P2/P3. Routing is in the message body, scoped by {{ #is_alert }} so resolved-events don't re-page.Downtime API with scope filters (env:prod service:checkout); document expected duration.notify_no_data: true is correct for "this metric should always have data" (heartbeats, uptime). For sparse metrics, notify_no_data: false plus a separate uptime monitor. Never default to true on every monitor — it pages on deploys.group by: host on auto-scaling fleets explodes alert count on scale-up. Group by service, env, cluster, customer-tier. Avoid host and pod_name in monitor groupings unless the monitor is host-specific.Test Notifications button or API POST /api/v1/monitor/{id}/notify. Verify routing, message rendering, runbook link, severity. Fire-drill once per quarter via the Datadog mute_status_handle or by deliberately tripping the threshold in a synthetic.runbook_url tag and the runbook link in the message. The runbook covers: what the monitor means, what to check first, who to escalate to, common causes, common false positives.Datadog has nine monitor types; most teams use Threshold for everything. That's how you get 4,000 monitors that don't catch real issues. Pick the type that matches the signal shape.
What are you alerting on?
├── A static SLA / SLO threshold (latency < 500ms, error rate < 1%)
│ → Metric Threshold monitor
│
├── A trend that crosses a threshold over time (disk fills, quota exhaust, cert expiry)
│ → Forecast monitor (linear or seasonal forecast)
│
├── A metric with a strong daily/weekly seasonality (traffic, signups)
│ → Anomaly monitor (agile, robust, or basic algorithm)
│
├── One host/pod/instance behaving differently from its peers
│ → Outlier monitor (DBSCAN, MAD, or scaledZ)
│
├── A condition that requires multiple signals to all be true
│ → Composite monitor (AND of two metric monitors)
│
├── An event happening (deploy, security finding, audit log)
│ → Event monitor or Event-V2 monitor
│
├── A log pattern occurring at rate
│ → Log monitor (don't use Threshold on a log-based metric — Log monitor is cheaper)
│
├── An external endpoint being reachable
│ → Synthetic monitor (browser or API test)
│
└── A process / service running on a host
→ Process monitor (legacy) or Service Check monitorRules of thumb:
Each tier ships with a fixed monitor pack. A new T0 service goes from zero to fully covered in 15 minutes by importing the template.
| Monitor | Type | Threshold | Window | Notify | |---------|------|-----------|--------|--------| | Availability (HTTP 5xx rate) | Metric Threshold | >0.5% over 5m | 5m | P0 → PagerDuty (urgent) | | p99 Latency | Metric Threshold | >1.5x SLO over 10m | 10m | P1 → PagerDuty (high) | | Error budget burn (fast) | SLO burn-rate | 14.4x burn over 1h | 1h | P0 → PagerDuty (urgent) | | Error budget burn (slow) | SLO burn-rate | 6x burn over 6h | 6h | P1 → PagerDuty (high) | | Saturation (CPU/mem) | Forecast | >85% in 24h | 24h forecast | P2 → Slack | | Dependency health | Composite | upstream availability < 99% AND request rate > 100/s | 5m | P2 → Slack | | Deploy regression | Anomaly | error rate +3σ post-deploy | 30m | P1 → PagerDuty (high) | | No-data (heartbeat) | Metric Threshold (notify_no_data) | no data for 5m | 5m | P1 → PagerDuty (high) | | Cost anomaly (AWS bill tag) | Anomaly | +2σ on 7d window | 24h | P3 → Slack digest |
| Monitor | Type | Threshold | Window | Notify | |---------|------|-----------|--------|--------| | Availability | Metric Threshold | >2% over 10m | 10m | P2 → Slack live | | p95 Latency | Metric Threshold | >2x baseline over 15m | 15m | P3 → Slack digest | | Error budget burn | SLO burn-rate | 6x burn over 6h | 6h | P2 → Slack live | | Saturation | Forecast | >90% in 48h | 48h forecast | P3 → Slack digest | | Deploy regression | Anomaly | error rate +3σ post-deploy | 1h | P2 → Slack live | | No-data | Metric Threshold | no data for 15m | 15m | P3 → Slack digest |
| Monitor | Type | Threshold | Window | Notify | |---------|------|-----------|--------|--------| | Availability | Metric Threshold | >5% over 30m | 30m | P3 → Slack digest | | Job failure (batch) | Event monitor | failed job event | event | P3 → Slack digest | | Cron heartbeat | Synthetic / heartbeat | missed schedule by 2x interval | 2x | P3 → Slack digest |
T3 (experiments, prototypes) get no monitors. If they need monitoring they're not T3.
The SRE workbook standard. Every SLO turns into two paired alerts: fast-burn (catches fast-burning incidents) and slow-burn (catches slow-burning ones). Single-window burn-rate alerts either page too late or page on noise.
Definitions (assuming 30-day SLO window, 99.9% objective, error budget = 0.1% = 43.2 minutes):
fast_burn_rate = 14.4 # exhaust full budget in (30d / 14.4) = 50 hours
slow_burn_rate = 6 # exhaust in 30d / 6 = 5 days
fast_window = 1h # short evaluation, fast detection
slow_window = 6h # longer evaluation, fewer false positivesThe two monitors per SLO:
yaml# Fast-burn (page urgently) type: slo alert slo: <slo_id> threshold: - critical: 14.4 - warning: 6 threshold_windows: - critical: 1h - warning: 5m # short-window check to confirm notify: "@pagerduty-{{service}}" message: "🔥 Fast burn: {{value}}x error budget consumption in last 1h" # Slow-burn (page during business hours) type: slo alert slo: <slo_id> threshold: - critical: 6 - warning: 3 threshold_windows: - critical: 6h - warning: 30m notify: "@slack-{{team}}" message: "🐌 Slow burn: {{value}}x error budget consumption in last 6h"
Burn-rate cheatsheet:
| Burn rate | Time to exhaust | Use as | |-----------|-----------------|--------| | 1x | 30 days (full window) | baseline (no alert) | | 2x | 15 days | tracking, no alert | | 6x | 5 days | slow-burn alert (P2) | | 14.4x | 50 hours | fast-burn alert (P1) | | 36x | 20 hours | critical fast-burn (P0) |
For availability SLOs, the good_events / total_events formulation maps directly to Datadog SLOs. For latency SLOs, define good = requests with p95 < threshold, total = all requests. Datadog supports both via SLO objects.
A pageable Datadog notification has eight required elements. Anything missing is a footgun for the on-call. Use this template literally.
markdown# 1. SEVERITY + ONE-LINE TITLE 🚨 [P1] checkout-svc p99 latency above SLO # 2. CURRENT STATE (template variable) Current value: {{value}}ms (threshold: 1500ms) Affected scope: {{scope.name}} env:{{env.name}} region:{{region.name}} # 3. TIME WINDOW Triggered at {{last_triggered_at}} Evaluation window: last 10 minutes # 4. RUNBOOK LINK (HARD REQUIREMENT) Runbook: https://runbooks.example.com/checkout/p99-latency Dashboard: https://app.datadoghq.com/dashboard/abc-checkout # 5. RECENT CHANGES (auto-injected via Datadog Events overlay) {{ #is_alert }} Last deploy: {{ event.deploy.version }} at {{ event.deploy.timestamp }} {{ /is_alert }} # 6. WHO TO PAGE (routing) {{ #is_alert }} @pagerduty-checkout @slack-checkout-oncall {{ /is_alert }} {{ #is_recovery }} @slack-checkout-oncall ← only post recovery to Slack, NOT PagerDuty {{ /is_recovery }} # 7. WHAT TO CHECK FIRST (3 bullets max) - Is upstream payment-svc healthy? (check service map) - Did a deploy land in the last 30 min? (releases dashboard) - Are we in a known traffic spike? (traffic dashboard) # 8. ESCALATION If unresolved after 15 min, escalate to @platform-team. Owner: {{tag.team}} | Tier: {{tag.tier}}
Datadog-specific tips:
{{ #is_alert }} blocks for alert-only content. Recovery messages should only post to Slack, never re-page PD.{{ #is_no_data }} is a separate state — the message should differ from is_alert.{{ scope.name }} resolves to the grouping; for multi-alerts it's the failing group, for simple alerts it's the whole monitor scope.Tags drive routing, downtime, dashboard filters, and cost attribution. A bad tag taxonomy makes the entire monitor estate brittle. Lock these five tags before designing any monitor.
| Tag | Required | Values | Purpose | |-----|----------|--------|---------| | env | Yes | prod, staging, dev, canary | Scope monitors; never alert on non-prod by default | | service | Yes | checkout, auth, search, ... | Routing, ownership, service map | | team | Yes | payments, platform, data, ... | Routing, on-call mapping | | tier | Yes | T0, T1, T2, T3 | Template inheritance, severity defaults | | version | Yes | git sha or semver | Deploy regression detection, downtime per release | | region | If multi-region | us-east-1, eu-west-1, ... | Scope, regional outage isolation | | customer_tier | If B2B | enterprise, pro, free | Prioritise enterprise-impacting alerts |
Hard rules:
env:prod (or explicit env) — no environment-blind monitors.service and team tags so routing works.user_id, request_id, session_id) — they don't help monitors and they explode metric cost.Service:Checkout ≠ service:checkout. Lowercase always.Composite monitors (monitor_a && monitor_b) reduce false positives by requiring multiple signals. They're underused because most teams default to standalone thresholds.
Pattern 1 — Error rate AND traffic floor. A 100% error rate on 3 requests/min is noise; on 10k requests/min it's an outage.
composite: error_rate > 5% AND request_rate > 100/sec for 5minPattern 2 — Latency AND deploy correlation. p99 latency rising AND a recent deploy = regression. Either alone is normal traffic variance.
composite: p99_latency > SLO * 1.5 AND deploys_in_last_30m > 0Pattern 3 — Multi-region quorum. Alert only when 2 of 3 regions are degraded.
composite: us_east_errors > 1% AND eu_west_errors > 1%Pattern 4 — Saturation AND queue depth. CPU high alone is fine if work is getting done; CPU high AND queue growing = real problem.
composite: cpu_avg > 80% AND queue_depth > queue_depth_baseline * 2Pattern 5 — Dependency degraded AND own service degraded. Don't alert when the upstream is down (alert the upstream's owners); only alert when the upstream's degradation impacts you.
composite: upstream_error_rate > 1% AND own_p99 > SLOMute the children. When a composite owns the signal, mute the underlying monitors so they don't double-page. Use Datadog's mute_status_handle or set the child monitor priority to "tracked, no notification."
Pages during planned maintenance burn trust. Schedule downtime — don't tell humans to ignore alerts.
Recurring downtime for deploy windows:
yamltype: downtime scope: "env:prod service:checkout deploy:active" recurrence: rrule "FREQ=WEEKLY;BYDAY=TU;BYHOUR=14" duration: 30m message: "Tuesday deploy window — alerts suppressed"
One-shot downtime for DB upgrade:
yamlscope: "env:prod database:rds-prod-001" start: 2026-05-10T02:00:00Z end: 2026-05-10T04:00:00Z mute_first_recovery_notification: true # don't celebrate recovery while still in maintenance
Smart mute for canary deploys: mute the canary's tag for 30 minutes after deploy, but keep the rest of prod alerting normally.
scope: "env:prod canary:true"
duration: 30m
trigger: post-deploy webhookMute on incident: during an active P0 incident, mute downstream child alerts so the war room isn't flooded with secondary effects.
scope: "incident:INC-1234" # via tag pushed to all downstream services
auto-unmute: 30 min after incident.resolved=trueAnti-pattern: human-applied mutes. "I'll mute it for an hour" gets forgotten. Always set an explicit end time.
Datadog's notify_no_data and no_data_timeframe are the most-misconfigured options.
Decision matrix:
| Signal | notify_no_data | no_data_timeframe | Notes | |--------|---------------|-------------------|-------| | Heartbeat (cron, scheduled job) | true | 2x interval | "Job runs every 1h" → no_data_timeframe = 2h | | Always-on service traffic | true | 10m | Genuine "no traffic" is an outage | | Sparse error metric | false | n/a | No errors = no data ≠ alert | | User-action metric (signups) | false | n/a | Quiet hours are normal | | Saturation metric | true | 30m | Agent died = no data = blind | | Synthetic check | true | 5m | Synthetic failure = no test = page |
Heartbeat pattern (the right way to monitor a cron):
metric: my.cron.heartbeat (statsd counter, sent at end of each run)
monitor: max(last_2h):default(my.cron.heartbeat{job:nightly-export}, 0) < 1
notify_no_data: true
no_data_timeframe: 120The default(metric, 0) trick converts "no data" into "value 0" so the threshold fires reliably. More robust than notify_no_data alone, which has subtle behaviour around evaluation windows.
Datadog billing has three big levers. Monitors interact with all of them.
Custom metrics: billed per unique timeseries (metric name × tag combo) per month. A monitor avg by (service, customer_id) over (...) with 10,000 customers = 10,000 timeseries per metric. Stop tagging by anything user-cardinality. Use service, env, region only.
Indexed logs: billed per log indexed (not just ingested). Log monitors index matched logs. A pattern like * indexes every log = bankruptcy. Always scope log monitors to a specific service + level + pattern.
Ingested traces: billed per million spans. Trace-based monitors (APM monitors) consume ingestion budget. Use head-based sampling to drop non-error traces; tail-based for nuanced sampling on errors.
The 80/20 cost audit:
Plan & Usage → Custom Metrics — top 10 metrics by timeseries countPlan & Usage → Logs — top 10 indexes by volume; cut indexing rules to scopePlan & Usage → APM — drop service catalog entries for retired servicesgroup_by: host on autoscaling fleets. Scale-up doubles your monitor count overnight; scale-down breaks no-data evaluation. Group by service, not host.notify_no_data: true everywhere. Pages on every deploy, network blip, agent restart. Use only for true heartbeats with a separate uptime monitor.env:prod scope. Monitor fires on dev's broken laptop. Always scope to env.user_id in a monitor scope = bankruptcy.{{ #is_recovery }} block.@pagerduty-X per service.avg over instead of min over for SLO checks. Avg masks brief spikes. SLO breaches are about thresholds being crossed, not averages — use min/max for breach detection.datadog_monitor resource or via API with audit log.evaluation_delay. When metrics arrive 30s late (common with batch ingestion), an alert evaluating the last minute sees zero data. Set evaluation_delay: 60 to compensate.env, service, team, tier, runbook_urlgroup_by clauses use stable dimensions; no host/pod_name groupings on autoscaling fleets| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | pass→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +41 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.