# OpenSearch Domain Idle

> 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.

Source: https://zop.dev/integrations/aws/recommendations/opensearch-domain-idle
Updated: 2026-08-19

---

## 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](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/managedomains-cloudwatchmetrics.html) 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

```bash
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 Maximum
```

`ClientId` 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](https://aws.amazon.com/opensearch-service/pricing/)) 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.
