Define monitoring dashboards, alerting rules, and SLO configurations as code that is version-controlled, reviewed, and deployed through CI/CD. Covers Terraform for monitoring resources, Grafana-as-code, Prometheus alerting rules, and the workflow that prevents dashboard sprawl and undocumented alert changes.
Monitoring as Code is the practice of storing monitoring configuration—dashboards, alerts, and metric definitions—in version-controlled text files rather than in UIs or proprietary binary formats. This approach makes monitoring infrastructure reproducible, auditable, and deployable like application code. It matters when teams need to maintain consistent alerting across environments, automate monitoring setup, or ensure that monitoring changes follow the same review and deployment process as application code.
Alert definitions are typically stored as structured text files in a repository. The most common format is YAML, but JSON is also used. An alert definition includes the condition that triggers the alert, the severity level, and metadata for routing and notifications.
name: "High CPU Usage"
description: "Instance CPU usage exceeds 80% for 5 minutes"
condition:
metric: cpu_usage
operator: ">="
threshold: 80
duration: 300
severity: "warning"
labels:
team: "backend"
service: "webapp"
Alert conditions require a metric name, an operator, and a threshold. The supported operators vary by system but typically include:
> Greater than< Less than>= Greater than or equal<= Less than or equal== Equal to!= Not equal toThe duration field specifies how long the condition must persist before triggering an alert, measured in seconds.
Alerts can be routed based on labels. A system might define routes that send different alerts to different channels:
name: "Critical Database Alert"
condition:
metric: database_errors
operator: ">="
threshold: 10
duration: 60
labels:
team: "database"
severity: "critical"
To manage alerts in a version-controlled manner, you can write a script that applies definitions from files to a monitoring system:
#!/bin/bash
# Apply all alert definitions from the alerts directory
for file in alerts/*.yaml; do
curl -X POST \
-H "Content-Type: application/yaml" \
-d "@$file" \
http://monitoring-api.example.com/v1/alerts
done
A common failure mode occurs when alert conditions reference metrics that don't exist in the backend. The system may silently ignore alerts with invalid metric names, leading to silent monitoring gaps.
Dashboards are defined as structured configuration files that specify which metrics to display, how to visualize them, and what data ranges to show.
title: "Application Performance Dashboard"
description: "Overview of key application metrics"
widgets:
- type: "graph"
title: "CPU Usage"
metric: cpu_usage
range: "last_1h"
aggregation: "avg"
- type: "table"
title: "Error Rates"
metrics:
- error_rate_5xx
- error_rate_4xx
range: "last_15m"
Widgets can be graphs, tables, or single-value displays. Each widget specifies:
When modifying dashboards, always version the configuration files. A dashboard file might be named application-dashboard-v1.yaml and application-dashboard-v2.yaml to track changes.
#!/bin/bash
# Deploy dashboard from YAML definition
curl -X PUT \
-H "Content-Type: application/yaml" \
-d @dashboards/application-dashboard.yaml \
http://monitoring-api.example.com/v1/dashboards/application
All monitoring definitions should be stored in the same repository as application code. This ensures that changes to monitoring align with code changes and are subject to the same review process.
A pre-commit hook can validate alert definitions before allowing commits:
#!/bin/bash
# Pre-commit hook to validate alert files
for file in alerts/*.yaml; do
if ! yamllint "$file"; then
echo "Invalid YAML in $file"
exit 1
fi
done
Use branch protection rules to require review before merging monitoring changes. This prevents accidental deployment of broken or incorrect alert configurations.
Alerts often depend on other alerts or metrics. A failure in one metric can cause a cascade of alerts.
# Primary alert
name: "Database Connection Pool Exhausted"
condition:
metric: database_pool_exhausted
operator: "=="
threshold: 1
duration: 60
# Secondary alert that depends on primary
name: "Application Down"
condition:
metric: app_status
operator: "=="
threshold: "down"
duration: 300
labels:
depends_on: "Database Connection Pool Exhausted"
Some systems support suppression rules that prevent alerts from firing during known maintenance windows:
name: "Maintenance Window Suppression"
condition:
metric: maintenance_window_active
operator: "=="
threshold: 1
duration: 3600
Monitoring systems track alert state including when alerts were triggered, acknowledged, or resolved. This information should be preserved and versioned alongside alert definitions.
Alerts are typically persisted in a database with fields like:
alert_id: Unique identifier for the alertstatus: Active, acknowledged, resolvedtimestamp: When state changedacknowledged_by: User who acknowledgedMonitoring changes should be deployed with the same care as application code. Changes can be rolled back if they cause issues.
# Deploy new alert configuration
curl -X POST \
-H "Content-Type: application/yaml" \
-d @alerts/new-alerts.yaml \
http://monitoring-api.example.com/v1/alerts
# If issues arise, rollback to previous version
curl -X DELETE \
http://monitoring-api.example.com/v1/alerts/new-alerts
curl -X POST \
-H "Content-Type: application/yaml" \
-d @alerts/old-alerts.yaml \
http://monitoring-api.example.com/v1/alerts
One of the most common silent failures is when alert definitions reference metrics that don't exist in the monitoring backend. The system may silently ignore these alerts, leaving teams unaware of missing monitoring.
Time range specifications in dashboards can silently default to incorrect values if an unsupported range is specified. For example, specifying "last_30d" when only "last_1h", "last_6h", "last_1d" are supported.
Labels used for routing alerts may be silently truncated or ignored if they exceed system limits. A label value of 256 characters might be silently truncated to 128 characters.
Alerts should have a defined lifecycle from creation to retirement. This includes:
#!/bin/bash
# List all active alerts that haven't been reviewed in 90 days
curl http://monitoring-api.example.com/v1/alerts \
| jq '.[] | select(.last_reviewed < "2023-01-01")'
Alert definitions should be tested before deployment. This can be done by:
#!/bin/bash
# Validate alert files and check for missing metrics
for file in alerts/*.yaml; do
echo "Testing $file"
yamllint "$file"
# Extract metrics from alert definition
metrics=$(grep -o 'metric: [a-zA-Z0-9_]*' "$file" | cut -d' ' -f2)
for metric in $metrics; do
if ! curl -f -s "http://monitoring-api.example.com/v1/metrics/$metric" > /dev/null; then
echo "Warning: Metric $metric not found"
fi
done
done
Alert definitions should include metadata about ownership, contact information, and documentation:
name: "High Memory Usage"
description: "Instance memory usage exceeds 85% for 10 minutes"
owner: "backend-team"
contact: "[email protected]"
documentation: "https://internal.company.com/docs/alerts/high-memory-usage"
condition:
metric: memory_usage
operator: ">="
threshold: 85
duration: 600
Monitoring configurations often vary between environments (dev, staging, production). Version-controlled configurations can support this through environment-specific files or templating.
# alerts/prod-alerts.yaml
name: "Production Critical Alert"
condition:
metric: critical_errors
operator: ">="
threshold: 5
duration: 300
labels:
environment: "production"
This approach ensures that monitoring configuration changes are tracked and deployed consistently across environments, maintaining the same monitoring coverage and alerting behavior.
This page was rewritten on 15 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