Alright, listen up. I've been woken up at 3 AM more times than I can count because of garbage alerts. The number one culprit, bar none, is lazy threshold configuration. Everyone sets up their monitoring, slaps in the defaults from the vendor blog post, and calls it a day. Then they wonder why they're drowning in noise and missing the real fires.
Tuning thresholds isn't about making your graphs look pretty; it's about aligning your alerts with actual user pain and system failure modes. This is a process, not a one-time task. Here's how I do it, the boring way that actually works.
First, you need a baseline. You don't just guess. For a week, you let those noisy alerts fire, but you **tag them**. Every single one. Your alerting system needs to support this. I use a simple annotation in Alertmanager or a tag in the alert itself. The categories are:
* **Actionable & Correct:** The system is broken, and you need to do something.
* **Actionable but Wrong Severity:** It's a real issue, but a P4, not a P1.
* **Non-Actionable Noise:** The metric breached a theoretical threshold, but nothing is actually degraded. This is your target for elimination.
After that week, you analyze. If 80% of your alerts are "Non-Actionable Noise," your thresholds are worthless. This is where the real work starts.
Let's take a concrete example: CPU usage. The default alert is always something brain-dead like `cpu_usage > 80%`. This is useless. A modern system can spike to 100% for minutes during a batch job with zero user impact. Instead, you need to alert on *sustained* high CPU *combined* with a symptom of user impact.
Here's a Prometheus-style rule that's more thoughtful:
```yaml
groups:
- name: node.rules
rules:
- alert: HostHighCpuLoad
expr: |
(100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)) > 80
and on(instance)
(rate(node_network_receive_bytes_total[5m]) < 1000)
and on(instance)
(rate(http_requests_total{code!~"5.."}[5m]) 80%) for 10 minutes, AND low network traffic, AND low successful HTTP requests. Might be a stalled process, not just load.'
```
See the difference? This only fires if high CPU is **paired with low traffic and low successful requests**. That suggests the system is choked and can't do work, not that it's busy. You tune the `80`, the `10m`, and the ancillary condition thresholds (`1000` bytes, `1` request) based on your baseline data.
The process is always the same:
1. **Measure the baseline** for the metric during known good and known bad states. Use percentiles (p95, p99), not averages.
2. **Set the threshold** between these observed bands, with a healthy buffer. If your app performs fine with latency at 500ms, but users complain at 1500ms, maybe alert at 1200ms.
3. **Add a duration clause (`for:`)**. Almost nothing should fire instantly. Spikes happen. 5 minutes is a good starting point for most system metrics.
4. **Correlate with a symptom.** High memory? Check for OOM kills or swap usage. High disk usage? Check the rate of growth, not the static percentage.
5. **Document the rationale.** In the runbook, state *why* this threshold exists and what the expected response is. If you can't write this, the alert shouldn't exist.
Finally, you make this part of your post-mortem process. Every incident review should ask: "Did our alerts fire correctly? Were they too noisy? Did we miss something?" Then you go back and adjust. It's a feedback loop. The goal is to get to a state where every page is a real page, and you can actually trust your dashboards.
This makes so much sense. Tagging alerts for a week to see what's actually actionable vs. noise sounds like a perfect way to get real data instead of just guessing.
I'm new to this, so I have to ask: what do you do with that "non-actionable noise" category after you've identified it? Do you just silence those specific alerts, or is it better to go back and adjust the threshold value itself?
Oh, that's such a key question. I've been wondering the same thing. In our Slack setup, I'd worry that just silencing a noisy alert might make me forget about it later. Could adjusting the threshold be better so you still get notified if things get *way* worse?
What happens if the baseline changes, like after a big update? Does the process start over?
You're on the right track, but tagging for a week and then analyzing is just the start. The real trap is thinking you can just set a threshold and be done with it.
Your baseline is a moving target. If you adjust a threshold based on a week of data and then deploy a new feature next month that changes load patterns, you've just reintroduced the noise. That's why most teams end up in a permanent cycle of tuning and silencing.
The better question is whether you're alerting on the right metric at all. A well-tuned threshold on a bad metric is just efficient nonsense. I'd rather have a noisy alert on a metric that actually correlates with user pain than a perfectly quiet alert on some abstract "system health" score the vendor sold you.
Just my 2 cents
Exactly. You're right that > a well-tuned threshold on a bad metric is just efficient nonsense. This is where having a solid RFP template for monitoring tools can save so much headache. It forces you to define what "user pain" looks like for your service before you even look at a dashboard.
When we evaluate vendors, we always ask them to map their default alerts and KPIs to specific, pre-defined failure scenarios from our side. If they can't, or if they're pushing a generic "health score," that's a red flag. The goal isn't a quiet pager, it's a pager that pages for a reason.
Ask me about my RFP template
You're right about the tagging process, but that analysis phase is critical. I've seen teams make the mistake of just silencing the "noise" alerts without understanding *why* they were noise.
Often, the "Non-Actionable Noise" category reveals a metric that's just volatile, not indicative of failure. In those cases, adding a simple moving average to your threshold calculation, or switching from an absolute to a percentile-based threshold, can turn that noise into a usable signal without completely discarding it.
The goal is to convert noise into a lower-severity indicator, not just delete it. That way you still have a record if the pattern changes dramatically.
—Anita