OpenSearch domains with no queries, no indexing and no CPU for 30 days
What does ZopNight detect here?
ZopNight flags an OpenSearch domain only when SearchRate and IndexingRate both sit below 0.10 per minute and CPU stays under 5% across 30 days of full coverage. A query-less domain that is still ingesting documents is treated as in use, and any missing series makes the rule abstain.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-104 |
| Category | idle |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | opensearch_idle.go |
Where it applies
Zero queries is not enough
Plenty of OpenSearch domains answer no searches and still work for a living: log-archive
sinks and ingest pipelines write documents all day without a single query arriving. For
a destructive “this domain is unused” finding, query silence is necessary but nowhere
near sufficient. The rule therefore demands three independent axes from the domain
metrics OpenSearch Service publishes go quiet together: SearchRate, IndexingRate, and CPUUtilization. The last one catches domains that
burn cycles on segment merges or replication despite no external traffic at all.
The exact floors
| Axis | Floor | Applies to |
|---|---|---|
SearchRate | below 0.10 per minute | Average and Maximum |
IndexingRate | below 0.10 per minute | Average and Maximum |
CPUUtilization | below 5% | Average and Maximum |
The 0.10 floors are expressed in the metrics’ native per-minute units, which makes them a deliberately tight bound of under 6 requests per hour. They are floors rather than hard zeros because these series carry trace noise (health-probe queries, internal housekeeping) on domains that are functionally dead.
Coverage matters as much as the value
Each series must actually cover the full 30 days, not merely read low. A thinly covered CPU series can coincidentally sit near zero during a partial window (mid instance-type change, or halfway through a blue-green cycle), and a month of inactivity cannot be proven from a week of data.
Every exit follows that fail-closed pattern: any of the three series missing, any axis at or above its floor, any window shorter than 30 days, or no resolved run-rate: each produces no finding rather than a hedged one.
Ask CloudWatch directly
aws cloudwatch get-metric-statistics \ --namespace AWS/ES --metric-name SearchRate \ --dimensions Name=DomainName,Value=my-domain Name=ClientId,Value=123456789012 \ --start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \ --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --period 86400 --statistics Average MaximumClientId is your account ID; OpenSearch publishes its CloudWatch metrics under both
dimensions. Repeat for IndexingRate and CPUUtilization.
The bill an idle domain keeps paying
An OpenSearch domain bills its full provisioned run-rate (instance node-hours times node count, plus EBS standing storage) whether it serves a thousand queries or none. The recoverable saving is that entire figure. Take a manual snapshot to S3 first, then delete the domain, or downsize the instance type and count if the workload shrank rather than vanished.