Running EC2 instances whose peak CPU stays under 8% in 95% of hours for 30 days
What does ZopNight detect here?
ZopNight flags running EC2 instances whose hourly peak `CPUUtilization` stays under 8% in at least 95% of measured hours over 30 days, with at least 168 hours of data and network traffic averaging under 5 MB per 5-minute sample in each direction. The saving is the compute cost; EBS volumes and Elastic IPs keep billing after a stop.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-179 |
| Category | idle |
| Severity | medium |
| Metric | CPUUtilization |
| Threshold | peak CPU < 8% in 95% of hours |
| Evaluation window | 30d |
| Source | ZopNight |
| Permissions used | ec2:DescribeInstances · cloudwatch:GetMetricStatistics · cloudwatch:GetMetricData |
Where it applies
A running instance bills for every second it is up, busy or not
The instance lifecycle guide
lists running as the billed state. CPU load does not enter into it: a machine sitting at 1% CPU
costs the same per hour as one at 90%. Stopping ends the compute charge, but
AWS notes
that attached EBS volumes and associated Elastic IP addresses persist through a stop, and those keep
their own charges.
Instances that nobody uses but nobody switched off are the target here. Instances already stopped belong to Idle EC2 Instance.
Looking at the hourly peaks, not the average
An average can hide a daily job. The hourly maximum shows it:
aws cloudwatch get-metric-statistics --namespace AWS/EC2 --metric-name CPUUtilization \ --dimensions Name=InstanceId,Value=i-0123456789abcdef0 \ --start-time 2026-08-26T00:00:00Z --end-time 2026-09-25T00:00:00Z \ --period 3600 --statistics MaximumCheck NetworkIn and NetworkOut the same way. With basic monitoring, each datapoint covers 5
minutes, so a byte count divided by 300 gives bytes per second.
The idle verdict, step by step
- The instance is
running, has no known recurring schedule, and is not already off most of the time by measured uptime. - The CPU data is recent and continuous, not a stale slice from a broken sync.
- Average
NetworkInandNetworkOuteach stay under 5 MB per 5-minute sample. - Hourly peak CPU is under 8%, and hourly average CPU under 5%, in at least 95% of measured hours, with at least 168 hours measured.
- No recurring daily peaks, sustained chatter or network bursts, and a confidence score of at least 50 once scattered spikes are weighed.
- A positive compute cost taken from the bill rather than a rack-rate estimate.
Instances it never tells you to stop
Names that mark access infrastructure (bastion, jump, jumpbox, vpn and similar), standby
roles (dr, standby, failover, replica, backup) or stateful data services are excluded,
because they are quiet by design. When the brief peaks recur at the same hours on most days, the
instance is running a daily task: ZopNight suggests a schedule that keeps that window on instead of
a stop. When the few peaks cluster near a month boundary, the finding warns you to check for a
month-end batch first.
The saving is compute only
saving = monthly compute cost of the instancestill billed after a stop = EBS volumes + any Elastic IPStopping it safely
- Confirm with the owner that nothing depends on the instance.
- Take an AMI so the change is reversible:
aws ec2 create-image --instance-id i-0123456789abcdef0 --name pre-stop-backup --no-reboot - Stop it:
aws ec2 stop-instances --instance-ids i-0123456789abcdef0 - After the retention period, terminate it and delete volumes you no longer need.
- If it turns out to have a purpose, put it on an off-hours schedule instead of running 24x7.