DEV Community

Cover image for The Hidden Cost Layers of EC2 (And Why Stopped Instances Still Drain Your Budget)
Rick Wise
Rick Wise

Posted on

The Hidden Cost Layers of EC2 (And Why Stopped Instances Still Drain Your Budget)

EC2 looks simple on the bill — until you pull it apart. What most teams see is a single line item for instance hours. In reality, every running (and even stopped) EC2 instance generates charges across multiple dimensions, and the overlooked ones tend to accumulate quietly.

Let's break down where EC2 costs actually come from, what keeps billing you after you click "Stop", and what you can do about it.


The Obvious: Instance Hours

On-Demand pricing ranges from roughly $0.0042/hr for a t4g.nano to over $30/hr for GPU-accelerated instances like the p4d.24xlarge. The exact rate depends on the instance family (general purpose, compute-optimized, memory-optimized, GPU, etc.), the instance size, and the region.

Most teams have a reasonable handle on this cost. The real surprises come from everything else attached to those instances.

The Persistent One: EBS Storage

Every EC2 instance boots from at least one EBS volume, and most production instances have additional data volumes attached. EBS is billed per GB per month, regardless of whether the instance is running:

Volume Type Price (us-east-1) Typical Use Case
gp3 (General Purpose SSD) $0.08/GB/mo Default for most workloads
gp2 (Previous Gen SSD) $0.10/GB/mo Legacy — still widely used
io1/io2 (Provisioned IOPS SSD) $0.125/GB/mo + IOPS charges High-performance databases
st1 (Throughput Optimized HDD) $0.045/GB/mo Big data, log processing
sc1 (Cold HDD) $0.015/GB/mo Infrequent access archives

When you stop an EC2 instance, you stop paying for compute hours. But every EBS volume attached to that instance continues to incur storage charges at the same rate. A stopped instance with 500 GB of gp3 storage still costs you $40/month in EBS alone — indefinitely.

This is one of the most common sources of invisible cloud waste. Teams spin up instances for a project, stop them "temporarily", and forget about them. Months later, those volumes are still quietly billing.

The Migration Opportunity: gp2 → gp3

If your account still has EBS volumes running on gp2, you're overpaying. AWS released gp3 as a direct replacement that is:

  • 20% cheaper per GB ($0.08 vs $0.10)
  • Higher baseline performance: 3,000 IOPS and 125 MB/s throughput included (gp2 baseline is only 100 IOPS per GB, minimum 100)
  • Independently tunable IOPS and throughput — with gp2, you have to increase volume size to get more IOPS

The migration is non-destructive and can be done live via ModifyVolume with no downtime. There's almost no reason not to migrate.

The Forgotten Ones: Unattached Volumes and Old Snapshots

When an EC2 instance is terminated, its root volume is deleted by default — but any additional EBS volumes may persist as unattached volumes (status: available). These volumes have no instance connected but are billed at the full storage rate.

Similarly, EBS snapshots accumulate over time. Each snapshot is billed based on the actual data blocks stored (not the full volume size), at $0.05/GB/month. A team that takes daily snapshots of a 500 GB volume without a retention policy can easily accumulate terabytes of snapshot storage within a year.

The Sneaky Ones: Elastic IPs and Data Transfer

Two more cost components are often overlooked:

Elastic IPs: A public IPv4 address attached to a running instance has historically been free, but as of February 2024, AWS charges $0.005/hr ($3.60/month) for every public IPv4 address — whether attached to a running instance or not. An Elastic IP on a stopped instance, or one not attached to any instance at all, costs the same.

Data Transfer: Outbound data transfer from EC2 to the internet is $0.09/GB (first 10 TB/month tier in us-east-1). Cross-AZ traffic within the same region costs $0.01/GB in each direction. These charges don't appear under "EC2" in Cost Explorer — they show up under "Data Transfer", making them easy to miss.

The Idle Tax

An instance that's running but not doing meaningful work is arguably the most expensive form of waste, because you're paying for both compute and storage. Common culprits:

  • Dev/staging instances left running outside business hours
  • Legacy instances that served a purpose months ago but were never decommissioned
  • Over-provisioned instances where a c5.4xlarge is running at 3% CPU because someone chose the instance size "just in case"

AWS CloudWatch metrics make it straightforward to identify instances with sustained low CPU utilization (below 5% over 14 days is a common threshold), but few teams audit this regularly.

What You Can Do

  1. Audit stopped instances: If an instance has been stopped for more than 30 days, either terminate it (after snapshotting the volumes if needed) or at minimum, detach and delete unused volumes.
  2. Migrate gp2 → gp3: Free performance improvement and 20% cost reduction on storage.
  3. Set snapshot retention policies: Delete snapshots older than 90 days unless compliance requires otherwise.
  4. Schedule dev/staging instances: Use Instance Scheduler or Lambda-based automation to stop instances outside working hours.
  5. Clean up unattached volumes: Any EBS volume with status available is costing you money for nothing.
  6. Review Elastic IPs: Release any EIPs not attached to running infrastructure.

None of these are difficult individually. The challenge is doing them consistently across every account and region. That's the kind of thing automation handles better than humans.


CloudWise detects idle EC2 instances, stopped instances with EBS volumes, unattached volumes, gp2-to-gp3 migration opportunities, old snapshots, and more — across all your regions and accounts. Try it at cloudcostwise.io

Top comments (0)