Production engineering guide for sre on call optimization covering patterns, implementation strategies, and operational best practices.
This page describes techniques for reducing noise and improving response quality in on-call workflows through systematic configuration of alerting, escalation policies, and incident response processes. It matters when teams experience high frequency of alerts, poor incident resolution times, or difficulty distinguishing between urgent and routine issues.
Use alert grouping to prevent multiple similar alerts from firing for the same root cause:
# Example alertmanager configuration for grouping
route:
group_by: ['alertname', 'job']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
The group_wait duration controls how long to wait before sending the first notification for a new group. The group_interval sets how often to resend notifications for the same group. If these are too short, alert fatigue increases.
Configure alerts based on statistical thresholds rather than fixed values:
# Example PromQL for time-based alert
rate(http_requests_total[5m]) > 100
Use the rate() function over a 5-minute window to smooth out temporary spikes. If the window is too short, alerts trigger on transient load fluctuations.
Set up silences that suppress alerts during known maintenance windows or known issues:
# Example silence creation using alertmanager API
curl -X POST http://alertmanager:9093/api/v1/silences \
-H "Content-Type: application/json" \
-d '{
"matchers": [
{"name": "job", "value": "webapp", "isRegex": false}
],
"startsAt": "2023-10-01T10:00:00Z",
"endsAt": "2023-10-01T14:00:00Z",
"createdBy": "ops-team",
"comment": "Maintenance window for webapp"
}'
Silences can silently fail if the matcher regex is incorrect. A common mistake is using isRegex: true when the value should match exactly.
Set up escalation chains that move issues from primary to secondary responders:
# Example escalation configuration
route:
receiver: "primary-team"
routes:
- match:
severity: "critical"
receiver: "critical-team"
repeat_interval: 15m
If repeat_interval is not configured, alerts may repeat too frequently or not at all. The default value varies between alertmanager versions.
Use specific time windows to escalate alerts based on elapsed duration:
# Example route configuration with time-based escalation
route:
routes:
- match:
severity: "warning"
receiver: "primary-team"
group_wait: 1m
group_interval: 5m
repeat_interval: 10m
continue: true
- match:
severity: "warning"
receiver: "secondary-team"
group_wait: 10m
group_interval: 10m
repeat_interval: 30m
If continue: true is missing, the second route may not execute. This prevents escalation from happening correctly.
Create explicit rules for when ownership of an alert should transfer:
# Example handoff configuration in a monitoring tool
handoff_rules:
- condition: "error_rate > 0.1 and response_time > 500ms"
target_team: "backend-team"
timeout_minutes: 30
- condition: "database_connection_errors > 5"
target_team: "database-team"
timeout_minutes: 15
The condition syntax is implementation-specific but typically supports logical operators and metric comparisons.
Define templates that guide responders through standard investigation steps:
## Incident Template
- **Start Time**: {{timestamp}}
- **Affected Systems**: {{affected_services}}
- **Current Status**:
- **Investigation Steps**:
1. Check logs for {{service}} in {{time_range}}
2. Verify metrics at {{metric_name}}
3. Review recent deployments
- **Escalation Path**:
- **Resolution Notes**:
Templates help maintain consistency across incidents but require maintenance when new patterns emerge.
Create a process to validate alerts before deploying them:
# Example alert validation script
curl -X GET http://prometheus:9090/api/v1/query \
-G --data-urlencode 'query=rate(http_requests_total[5m]) > 100' \
-H "Accept: application/json"
If the query returns no results, the alert may never fire. If it returns unexpected values, alerts may trigger incorrectly.
Set thresholds that account for expected variance:
# Example alert with confidence bounds
http_requests_total > 100 + 2 * stddev(http_requests_total[1h])
The standard deviation calculation requires sufficient historical data to be meaningful. If the window is too short, bounds become unreliable.
Define who responds to alerts during different time periods:
# Example schedule definition in a rotation system
schedule:
- name: "primary-shift"
members: ["alice", "bob", "charlie"]
start_time: "09:00"
end_time: "17:00"
timezone: "America/New_York"
Timezone conversion errors can cause alerts to go to the wrong person. If timezone is omitted, default behavior varies between implementations.
Set up periods where on-call responsibilities are suspended:
# Example off-call configuration
off_call_periods:
- start: "2023-10-01T22:00:00Z"
end: "2023-10-02T06:00:00Z"
reason: "Weekend maintenance"
excluded_teams: ["database-team"]
If excluded_teams is not supported, all teams may receive alerts during off-hours.
Control how frequently alerts can be sent for the same issue:
# Example rate limiting configuration
receivers:
- name: "primary-team"
webhook_configs:
- url: "http://webhook.service"
send_resolved: true
rate_limit: 10/hour
The rate limit applies per receiver, not per alert. If multiple receivers are configured, each must be limited independently.
Prevent identical alerts from being sent to the same channel:
# Example deduplication configuration
route:
group_by: ['alertname', 'job']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
If group_by is missing, alerts may not be grouped properly. This leads to duplicate notifications.
Include metadata that helps responders quickly understand the problem:
# Example alert with descriptive labels
alert: "High Database Query Latency"
expr: "pg_stat_statements_mean_time > 1000"
labels:
severity: "warning"
team: "database"
service: "postgres"
component: "query-engine"
annotations:
summary: "Database queries taking longer than expected"
description: "Average query time is {{value}}ms, exceeding threshold of 1000ms"
If annotations are not supported, alert text becomes less helpful. If labels are missing, responders must manually determine which system is affected.
Ensure alerts provide enough context to start investigation:
# Example alert with action links
alert: "Service Degradation Detected"
expr: "http_response_time_seconds > 10"
labels:
severity: "critical"
team: "frontend"
annotations:
summary: "Frontend service response time degradation"
description: "Response times above 10s for more than 5 minutes. Check logs at {{url}}"
runbook: "https://internal.wiki/service-incident/runbook"
If runbook is not supported, responders must find documentation manually.
Set up validation for configuration file changes:
# Example config validation script
curl -X POST http://alertmanager:9093/-/reload
if [ $? -ne 0 ]; then
echo "Configuration reload failed"
exit 1
fi
If the reload fails silently, alerts continue with outdated rules. The API endpoint returns HTTP 200 on success but may not validate content.
Check alert expressions before deployment:
# Example validation using promtool
promtool check rules /path/to/rules.yaml
Promtool reports syntax errors but not semantic correctness. An expression that compiles but produces no results will silently fail.
Set up alerts for when escalation chains do not work:
# Example monitoring of escalation success
alert: "Escalation Chain Failed"
expr: "increase(alerts_escalated_total[1h]) == 0"
labels:
severity: "warning"
team: "sre"
annotations:
summary: "No alerts escalated in the past hour"
If the metric alerts_escalated_total is not available, escalation failures go unnoticed.
Set up actions that trigger when specific conditions are met:
# Example automated remediation
route:
match:
severity: "warning"
receiver: "auto-remediate"
continue: true
receivers:
- name: "auto-remediate"
webhook_configs:
- url: "http://remediation-service"
send_resolved: true
payload: {
"action": "restart",
"target": "{{labels.job}}"
}
If the webhook target is unreachable, remediation fails silently. If send_resolved is not configured, responders never know when actions completed.
Monitor the alerting system itself:
# Example monitoring of alertmanager health
alert: "Alertmanager Down"
expr: "up{job='alertmanager'} == 0"
labels:
severity: "critical"
team: "sre"
annotations:
summary: "Alertmanager service is down"
description: "Alertmanager instance at {{labels.instance}} has been unreachable for 5 minutes"
If up metric is not available, monitoring of the alerting system itself fails. The job label must match exactly what's configured.
Misconfiguring group_by causes alerts to split unnecessarily:
# Example incorrect grouping that creates many groups
route:
group_by: ['alertname', 'job', 'instance'] # Too granular
If group_by is too specific, each instance gets its own alert group. If it's too broad, related issues get merged incorrectly.
Using inappropriate time windows in expressions causes alerts to trigger at wrong times:
# Example problematic expression
rate(http_requests_total[1m]) > 100 # Too short window
The 1m window may not capture steady-state behavior, causing false positives. The 5m window is usually more appropriate for rate-based alerts.
Confusing group_interval with repeat_interval leads to incorrect escalation timing:
# Example incorrect interval configuration
route:
group_wait: 30s
group_interval: 10m # Repeats group notifications
repeat_interval: 10m # Repeats individual alert notifications
If both intervals are set to the same value, escalation timing becomes confusing. The group_interval controls how often a notification is sent for a group, while repeat_interval controls how often a specific alert repeats.
Failing to specify error handling in alert expressions creates silent failures:
# Example expression that silently ignores errors
http_requests_total > 100
If http_requests_total is not available, the expression evaluates to empty results without notification. Always include error checking in critical alerts.
Using inconsistent labels across alert definitions creates confusion:
# Example inconsistent label usage
labels:
severity: "critical" # Consistent
team: "backend" # Consistent
service: "webapp" # Inconsistent with other alerts
If the same system is labeled as service: "webapp" in some alerts and application: "webapp" in others, filtering becomes difficult.
Creating alerts that depend on other systems without proper dependencies:
# Example alert that fails when dependent system is down
alert: "Database Connection Errors"
expr: "database_connection_errors > 5"
labels:
severity: "warning"
service: "postgres"
If the metrics source for database_connection_errors is unavailable, this alert becomes useless. Always include checks for data availability.
The key to on-call optimization lies in systematic configuration that prevents both alert fatigue and missed incidents. The patterns described here apply broadly across monitoring systems, but exact implementations vary by tool. Focus on clear grouping, appropriate thresholds, and consistent labeling to create reliable on-call experiences.
This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy