How to Set Up Alerts a Modern SRE's Guide
The usual alerting failure doesn't start with an outage. It starts with a team that already has alerts, already has dashboards, and still gets surprised. A database slows down, a cron job stops reporting, or a site returns broken pages while infrastructure metrics stay green. Then the on-call phone lights up for the wrong things, or stays silent for the actual one.
That's the gap this guide closes. How to set up alerts isn't about checking a box in a settings panel. It's about deciding what deserves human attention, routing it to the right person, and making sure every notification contains enough context to act fast. Teams that want calmer incident response usually need two things at once: sharper technical rules and better operational hygiene. That's also why burnout prevention matters here. Good alerting protects systems, but it also protects the humans carrying the pager, which is why these evidence-based prevention tips are worth keeping close when reshaping an on-call culture.
A production-ready setup also has to move beyond “send everything to Slack.” Smart alerting depends on baselines, suppression, escalation logic, and repeatable configuration. For a good framing of why rule quality matters more than clever automation layered on top of noisy checks, this piece on why smart alerts beat smart algorithms is a useful companion.
Table of Contents
- From Alert Fatigue to Actionable Insight
- Choosing What to Monitor The Right Metrics and Thresholds
- Creating Your First Monitors Uptime Infra and Cron Jobs
- Wiring Notifications Channels Routing and Escalations
- Taming the Noise Suppression Deduplication and Tuning
- Automating Your Alerts with Terraform and APIs
- Validating and Maintaining Your Alerting Strategy
From Alert Fatigue to Actionable Insight
The worst alerting systems are noisy in a very specific way. They interrupt often, explain little, and train responders to mistrust the signal. A team gets paged at night for CPU spikes that self-resolve, then misses the checkout error burst that harms users.
A good system does the opposite. It sends fewer alerts, but each one maps to a service, an owner, and a likely action. That shift turns alerting from a side task into an engineering discipline.
Practical rule: if an alert doesn't answer “what is broken, who owns it, and what should happen next,” it isn't ready for production.
That mindset changes setup decisions immediately. Teams stop leading with host metrics and start with service health. They stop treating every threshold breach as urgent. They stop broadcasting every event to every channel.
The useful mental model is simple:
| Alerting approach | What it feels like on call | What it leads to |
|---|---|---|
| Static, host-first, noisy | Constant interruptions | Ignored alerts and slow triage |
| Service-first, routed, contextual | Fewer interruptions | Faster acknowledgement and clearer ownership |
The difference usually isn't more tooling. It's better design. That means baselines before thresholds, ownership before notifications, and workflows before paging.
Choosing What to Monitor The Right Metrics and Thresholds
Start with a failure your users would notice. Checkout requests time out. Login succeeds but takes eight seconds. A backup job never finishes, so the restore point is stale by morning. Those are the conditions worth alerting on first.
Teams get into trouble when they build monitors around what is easy to graph instead of what breaks the service. CPU, memory, and disk still matter, but they are supporting signals. They rarely answer the first on-call question, which is whether customers can use the system.
Start with service impact
Map each business service to the few signals that show it is healthy or failing. For a public API, that usually means request rate, error rate, and latency. For a scheduled workflow, it means a successful completion signal within an expected time window. For a queue worker, it may be backlog growth, processing time, and consumer errors.

Two frameworks keep this disciplined:
- RED for user-facing services: rate, errors, duration
- USE for infrastructure resources: utilization, saturation, errors
Use both, but do not give them equal weight in paging. RED signals usually belong closer to the top of the alert stack because they track customer impact. USE signals help with diagnosis and capacity risk. A healthy node can still serve a broken application, and a hot node may be harmless during a traffic spike if the service stays within error and latency targets.
A simple monitor design path looks like this:
Name the service
- Checkout API
- Public website
- Nightly billing pipeline
Choose the symptom that matters
- Error rate rises above normal
- P95 latency stays high
- Expected completion or heartbeat is missing
Attach the likely supporting evidence
- Database saturation
- Container restarts
- Disk growth
- Queue depth
That structure also translates well to alerts as code. If a service already has an owner, an SLO, and a small set of agreed signals, writing the monitor in Terraform or creating it through the Fivenines API becomes straightforward. If those decisions are fuzzy, the UI will not save you. It just lets you create noisy alerts faster.
For systems where load changes quickly, throughput measurement for production systems helps explain whether rising latency points to normal demand growth, saturation, or an actual fault.
Build thresholds from history, not guesses
Bad thresholds usually come from a planning meeting where nobody has looked at the last 30 days of behavior. Good thresholds come from traffic patterns, deployment timing, cron duration, and known maintenance periods.
Baselines come first. Thresholds come second.
That rule prevents a lot of avoidable noise.
Use historical data to answer a few practical questions before you set anything to page:
- What does normal peak traffic look like by hour and region?
- How long does this batch job take on a good day and a bad day?
- How noisy is this metric during deploys?
- Does the metric recover on its own within a minute or two?
Threshold type matters as much as threshold value. Static limits are fine for clear failure states, such as "no heartbeat for 15 minutes" or "disk free space below a safety floor." They work poorly for metrics that swing with traffic. For latency, throughput, and queue depth, sustained deviation from a baseline is often better than a fixed number.
A few examples make the trade-off clear:
- Website latency: alert on tail latency such as p95 or p99. Mean latency hides slow requests that users feel.
- Disk capacity: alert on growth trend and time-to-full risk, not only a high-water mark.
- Cron jobs: alert on missed heartbeat or run time exceeding the normal window. Exit code alone misses hangs and partial failures.
- Error rate: page on sustained elevation over a short rolling window. A single burst during deploy rollback may belong in chat, not on a pager.
A planning table makes bad choices obvious:
| Target | Better alert signal | Weak alert signal |
|---|---|---|
| Website | Error rate, latency, content check | Host CPU alone |
| Database-backed app | Query failure symptoms, API duration | Memory graph without service context |
| Backup script | Heartbeat missed, run exceeded expected window | Server still online |
Severity should follow impact and urgency. Warning conditions can create a ticket or post to chat. Critical conditions should page the team that can act. In Fivenines, that usually means setting different thresholds for the same monitor and routing them to different destinations instead of treating every breach as an incident.
One last rule matters in production. If a monitor cannot be expressed clearly enough to version in code, review in a pull request, and recreate through an API, the definition is probably still too vague. Tight monitor design makes automation easier, and automation exposes weak alert logic early.
Creating Your First Monitors Uptime Infra and Cron Jobs
The easiest way to make alerting real is to build three monitor types that cover most production environments: a website check, an infrastructure monitor, and a heartbeat for scheduled work. That foundation catches a large share of incidents without creating a giant ruleset on day one.
A dashboard view helps operators see how these monitors fit together in practice.

Website uptime that checks the experience
A website monitor should do more than ask whether a port answers. It should verify that the endpoint responds correctly, from more than one region, and within acceptable latency. A useful check combines:
- Availability: the endpoint responds
- Content validation: the expected page or API response is present
- Performance: response time stays within the normal range
- Failure confirmation: don't page on a single transient probe
For operators comparing patterns, this guide to website uptime monitoring software shows why synthetic checks are more useful when they validate content and timing, not just reachability.
A strong website alert message includes the failing region, the last known good state, and a link to the dashboard. Without that, the responder still has to start from zero.
Infrastructure monitoring on a real host
Infrastructure monitoring becomes valuable when it reflects the workload, not only the box. On a Proxmox host running containers, that usually means tracking host metrics and container-level behavior together. CPU, memory, disk I/O, and network data matter, but they matter more when tied to the service generating them.
A sensible setup for a noisy containerized workload watches:
- Host pressure: CPU saturation, memory pressure, disk activity
- Container health: restart behavior, resource spikes, abnormal steady-state changes
- Dependency clues: whether the host issue is local or shared across services
A tool like Fivenines can combine Linux host telemetry, container monitoring, Proxmox visibility, uptime checks, and cron tracking in one place, which makes it easier to build service-aware alert rules instead of stitching together disconnected monitors.
The next step is to watch a setup walkthrough before building notification logic.
Heartbeat monitoring for scheduled work
Cron jobs often fail unannounced. That's why heartbeat monitoring belongs in the first wave of alerting. The monitor shouldn't only ask whether the server is up. It should expect a signal from the job itself.
A production pattern looks like this:
| Job type | What to monitor | Why it works |
|---|---|---|
| Nightly backup | Heartbeat on successful completion | Confirms work finished, not just started |
| Report generator | Expected completion window | Catches stuck jobs |
| Cleanup task | Missing run within schedule | Finds silent scheduler failures |
A cron monitor should represent the contract of the job. “Completed on time” is a better contract than “process existed briefly.”
That distinction matters. Plenty of jobs start and still fail the business outcome.
Wiring Notifications Channels Routing and Escalations
At 2:13 a.m., a payment API starts timing out. The alert fires. It lands in a general Slack channel with 200 people in it, no one owns it, and the only person awake has no access to the service. The monitor worked. The incident response path failed.
Notification design decides whether an alert turns into action or into noise. In production, routing needs to follow ownership, severity, and time-to-acknowledge. If any of those are missing, teams either over-page everyone or overlook real failures.
Route by ownership, then by impact
A shared channel is fine for low-risk visibility. It is a poor default for incidents. Billing alerts should go to the team that owns billing. Storage capacity alerts should go to the infrastructure team. Missed job heartbeats should go to the team that owns the business process, not whoever happens to watch the ops room.
A practical routing model usually starts with service boundaries and business impact:
- Public web tier: application or platform on-call
- Databases and storage: infrastructure owner or database on-call
- Cron and business workflows: team responsible for the job outcome
- Customer-facing status checks: support, operations, or incident channel, depending on severity

For MSPs and small platform teams, single-channel delivery is usually the first thing that breaks. Chat gets muted. Email sits unread. Phones go into Do Not Disturb. Production alerting needs at least one primary path and one fallback path for high-severity events.
In Fivenines, that often means sending lower-severity notifications to chat or email, while routing high-severity failures to the on-call path with acknowledgement tracking and escalation timers. The point is not to send more messages. The point is to make sure the right person gets one message they cannot reasonably miss.
Separate severity from channel
Severity answers, "How urgent is this?" Channel answers, "How do we reach the responder?" Treating those as separate controls keeps the policy readable and easier to maintain.
A workable pattern looks like this:
Medium severity
- Notify the owning team in chat
- Include monitor context, dashboard link, and runbook
- Wait for acknowledgement for a defined window
High severity
- Send immediately to chat and the paging path
- If no acknowledgement arrives in time, trigger SMS
- If it still sits unacknowledged, page backup on-call or place a voice call
Resolved
- Send the recovery notice to the same audience
- Skip broad recovery messages for low-priority events that never needed immediate action
This structure sounds simple, but the trade-off matters. If every warning pages the primary on-call, people learn to ignore pages. If nothing escalates beyond chat, teams discover failures hours later. Good alert routing puts interruption where it belongs.
Build for escalation failure, not just alert delivery
A lot of setup guides stop at "connect Slack" or "add email recipients." That is table stakes. Production systems need retries, acknowledgement handling, and escalation logic that still works when the first channel fails or the first responder is unavailable.
I prefer to treat this as configuration, not as UI-only setup. The routing rules, channel bindings, and escalation steps should be versioned alongside infrastructure changes. Teams already doing infrastructure automation can fold notification policy into the same review process they use for deployments and network changes. If that workflow is new to your team, this guide to Terraform infrastructure automation patterns is a good reference point for standardizing it.
Alerts as code also makes dynamic routing realistic. A monitor for prod-payments can route to the payments on-call schedule, while the same monitor definition in staging can post only to a team channel. That reduces manual drift and avoids the common failure mode where production changes but notification paths do not.
The right person should receive one useful alert. Everyone else should stay out of the blast radius.
Grouping still matters here, even before noise tuning. If one database failure creates separate host, disk, latency, and application alerts, the notification policy should direct them into one incident path instead of four parallel interruptions. Routing and escalation work best when they are attached to service ownership, not to isolated symptoms.
Taming the Noise Suppression Deduplication and Tuning
A noisy alerting system fails in a very specific way. The team stops trusting pages, acknowledges them on autopilot, and misses the one incident that needed immediate action. Good tuning fixes that by reducing interruptions to the alerts someone can act on.
The goal is not fewer alerts at any cost. The goal is fewer low-value alerts, fewer duplicates, and clearer signals during a real incident.
Suppression needs rules, not guesswork
Production changes create expected noise. Node reboots, failovers, schema changes, and deploys can all trip monitors that are technically correct but operationally useless for that window. Those alerts should be suppressed on purpose.
In practice, three controls do most of the work:
- Maintenance windows: mute monitors during scheduled work
- Grouping and deduplication: combine repeated symptoms that share the same incident
- Recovery thresholds: require stability before resolving, so alerts do not flap
I treat these as part of monitor design, not cleanup after the fact. If a database restart during a maintenance window still pages the team, the monitor is incomplete.
Fivenines is a useful example here because the tuning choices map directly to how responders work. A host outage can trigger infrastructure, application, and job-failure signals at the same time. Grouping those into one incident path keeps the on-call engineer focused on diagnosis instead of triage. Deduplication matters even more for retries and repeated failures, where the tenth alert rarely adds information the first one did not already provide.
Recovery logic deserves extra attention. Services often wobble after a deploy or after a dependency comes back online. If a monitor resolves the moment the metric dips below the threshold, then fires again 30 seconds later, the responder gets trapped in a fire, resolve, fire loop. A sustained recovery requirement cuts that churn.
Write alerts for the first minute of the incident
Noise is not only a volume problem. It is also a clarity problem.
An alert that says only "high error rate detected" creates extra work at the worst time. The responder still has to figure out which service is failing, how bad it is, how long it has been happening, and where to start. That lookup tax adds minutes to every incident.
Useful alerts include:
- Service or component name
- Exact symptom and threshold
- How long the condition persisted
- Dashboard link
- Runbook link
- Related dependency or correlation hint
A short comparison shows the difference:
| Weak alert | Better alert |
|---|---|
| API error threshold exceeded | Checkout API 5xx rate is above threshold for 10 minutes in production. Database latency is also high. Dashboard and runbook attached. |
That extra context changes behavior. The responder can open the right dashboard, check the likely dependency, and decide whether to mitigate or escalate without asking basic questions first.
Tune from incidents, then remove what nobody uses
The fastest way to improve alert quality is to review every noisy incident and every missed incident. If a page fired and nobody took action, either the threshold is wrong, the routing is wrong, or the monitor should not page at all. If an outage happened without a useful alert, coverage or confirmation logic is missing.
I also recommend tracking which alerts get acknowledged and closed with no follow-up work. Those are strong candidates for downgrade, grouping changes, or deletion.
An automation-first approach becomes critical. If suppression rules, dedup keys, and recovery settings live in code, teams can review tuning changes the same way they review infrastructure changes. That prevents the common drift where someone silences a noisy monitor in the UI, nobody records why, and the setting becomes permanent.
Automating Your Alerts with Terraform and APIs
A team with ten services can tolerate some manual alert setup. A team with fifty services, multiple environments, and rotating ownership cannot. At that point, the true risk is not just wasted time in the UI. It is drift, hidden changes, and pages that no longer match the system you are running.
Why alerts as code matters
Alert rules belong in version control for the same reasons Terraform state, Kubernetes manifests, and CI pipelines do. You need review, repeatability, rollback, and a change history that survives team turnover. If a paging threshold changes before an incident, responders should be able to find that change in a pull request instead of guessing who clicked what.

The failure modes are familiar:
- Environment drift: production has monitors that staging never received
- Undocumented edits: someone changed a threshold in the UI during a noisy week and never wrote it down
- Weak auditability: incident review shows an alert behaved differently, but nobody can trace when the rule changed
- Slow service onboarding: every new app needs the same monitor set, channel routing, and tags created again by hand
A useful reference on Terraform infrastructure automation for repeatable operations fits here because alert definitions usually change alongside infrastructure, deployment patterns, and service ownership.
A practical Terraform pattern
Start with modules, not one-off resources. A reusable module for an uptime check, a Linux host alert set, or a cron heartbeat gives teams a standard baseline and still leaves room for service-specific thresholds.
A workable structure looks like this:
- Module per monitor type
- uptime check
- infrastructure health policy
- cron or heartbeat monitor
- Variables per environment
- service name
- environment
- threshold values
- notification route
- escalation policy
- Shared metadata
- owning team
- severity
- business service
- runbook URL
That model pays off during review. A threshold change is visible in a diff. A route change from Slack-only to PagerDuty plus SMS is visible in a diff. If someone accidentally removes a production monitor, the rollback path is clear.
Fivenines is a good example of why this matters in practice. If your team is using Fivenines for uptime, Linux server checks, and scheduled job monitoring, codifying those monitor definitions keeps every environment aligned instead of relying on the UI as the source of truth.
Where APIs fit better than Terraform
Terraform handles long-lived monitors well. APIs are a better fit when monitor lifecycle follows application lifecycle.
Use the API when your system creates and destroys targets continuously, such as preview environments, customer-isolated deployments, or service-discovery-driven checks. In those cases, waiting for a Terraform plan and apply cycle is often the wrong operational trade-off. You want the monitor created at provisioning time, tagged correctly, and routed to the right team immediately.
A practical split looks like this:
| Use case | Better fit |
|---|---|
| Stable production monitors | Terraform |
| Shared baseline monitors across environments | Terraform |
| Ephemeral preview environments | API |
| Per-customer or runtime-created checks | API |
| Bulk rule updates across many services | Terraform or API, based on your workflow |
The rule I recommend is simple. Terraform defines the baseline. APIs handle short-lived or event-driven alert creation.
Use automation for routing too
Teams often stop at creating monitors in code and leave routing logic in the UI. That is where inconsistency returns. Routing rules, channel selection, and escalations should be managed with the same discipline as the monitor itself.
For example, a payment service monitor might page the primary on-call in production, send Slack notifications only in staging, and escalate to a secondary team if the alert stays open for fifteen minutes. Those are not UI details. They are operating rules. Keep them versioned and reviewable.
The result is a system that scales cleanly. New services inherit baseline coverage. New environments get the same rules on day one. Incident review gets a reliable record of what changed and why.
Validating and Maintaining Your Alerting Strategy
At 2:13 a.m., the question is not whether the monitor exists. The question is whether it fired for the right reason, reached the right person, and gave them enough context to act fast.
That is why validation has to be part of the alert design, not an afterthought. A production alerting system needs regular failure testing, review after real incidents, and cleanup work that removes rules nobody trusts. If a monitor only looks good in the UI, it is unfinished.
I treat alert validation as an operating routine:
- Test monitors on purpose: trigger controlled failures, missed heartbeats, dependency loss, and notification paths
- Review alert behavior after incidents: false positives point to bad thresholds or missing suppression, missed incidents expose coverage gaps
- Prune stale rules: if an alert never changes what the responder does, delete it or downgrade it
- Assign clear ownership: every alert needs a team that tunes it, documents it, and answers for it during review
The important trade-off is coverage versus credibility. More alerts can look safer on paper, but every noisy rule trains people to ignore the next page. Fewer, sharper alerts usually produce better incident response.
This is also where alerts as code pays off again. Store monitor definitions, routing rules, and ownership tags in Terraform or manage them through the API, then review changes the same way you review application config. That gives the team a record of who changed a threshold, when an escalation policy was edited, and whether a stale monitor was ever cleaned up.
Run a simple validation loop every quarter, and after major service changes:
- Pick a monitor from each critical service
- Force the failure condition in a safe test window
- Confirm the alert fired with the expected labels, severity, and runbook context
- Verify routing, escalation timing, and auto-resolution behavior
- Update thresholds or ownership if the result was noisy, late, or unclear
In Fivenines, that means checking more than whether a monitor turned red. Confirm that the right channel received the alert, the correct team owns it, and the escalation path still matches how the service is operated today. Teams change. Services move. Alert routing has to keep up.
The payoff is simple. On-call engineers trust what wakes them up, responders spend less time sorting noise from signal, and incident review turns into system improvement instead of blame or guesswork.