<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>CCA Blog</title>
        <link>https://cca.dragonfractal.com/blog/</link>
        <description>AWS &amp; Azure cost optimization guides from Cloud Cost Analyzer</description>
        <lastBuildDate>Sat, 25 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[We ran 89 cost checks on one AWS account: $32,000/year in findings, ranked]]></title>
            <link>https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account</link>
            <guid>https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account</guid>
            <pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[One scan, 89 checks, 1,358 resources in a single region. It surfaced 403 findings worth about $2,681/month in potential savings. Here is the full ranked breakdown, and the honest caveats about what that number really means.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> we pointed all 89 checks at one real AWS account, 1,358 resources in a
single region. The scan returned <strong>403 findings</strong> worth about <strong>$2,681/month</strong>, roughly
<strong>$32,000/year</strong>, in potential savings. This is the raw surface area of waste a single account
carries. It is not a promise you will bank all of it, and below I am going to be honest about why.</p>
<p>If you want the other side of this, where we actually fixed things and watched the invoice drop,
read the <a class="" href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study">realized case study</a>. This post is the opposite
end: not "here is what we saved," but "here is everything one scan finds before you touch
anything."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-account">The account<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#the-account" class="hash-link" aria-label="Direct link to The account" title="Direct link to The account" translate="no">​</a></h2>
<p>A normal setup. EC2 for pipelines and services, Aurora and RDS, ElastiCache, a pile of Lambda
functions, an EKS cluster, and the usual sediment of resources nobody has looked at in a year.
One region, us-east-1. We ran the full rule set once, read-only, and looked at what came back.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="first-the-honest-part">First, the honest part<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#first-the-honest-part" class="hash-link" aria-label="Direct link to First, the honest part" title="Direct link to First, the honest part" translate="no">​</a></h2>
<p>That $2,681/month is a <strong>gross, identified</strong> number, not a realizable one. Two reasons it is not
a shopping total you get to add up:</p>
<ol>
<li class=""><strong>The big line items overlap.</strong> A single idle EC2 instance can show up three ways: buy a
Savings Plan, cover it with a Reserved Instance, or right-size it. Those are not additive. You
pick one. A cost tool will happily count all three.</li>
<li class=""><strong>Some findings are hygiene, not dollars.</strong> Seven unused security groups and four idle Step
Functions saved $0.00 each. They are worth cleaning up, but they do not move the bill.</li>
</ol>
<p>So read the ranking below as a map of where the account is soft, not a check you can cash. With
that said, the map is the useful part.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-ranking-real-numbers">The ranking (real numbers)<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#the-ranking-real-numbers" class="hash-link" aria-label="Direct link to The ranking (real numbers)" title="Direct link to The ranking (real numbers)" translate="no">​</a></h2>
<table><thead><tr><th>Finding</th><th style="text-align:right">Count</th><th style="text-align:right">$/mo</th></tr></thead><tbody><tr><td>Spot-eligible workloads</td><td style="text-align:right">11</td><td style="text-align:right">$726.59</td></tr><tr><td>Savings Plan opportunities</td><td style="text-align:right">18</td><td style="text-align:right">$554.85</td></tr><tr><td>Idle / low-CPU EC2</td><td style="text-align:right">7</td><td style="text-align:right">$493.20</td></tr><tr><td>ElastiCache, low connections</td><td style="text-align:right">4</td><td style="text-align:right">$428.34</td></tr><tr><td>Reserved Instance coverage gaps</td><td style="text-align:right">14</td><td style="text-align:right">$382.23</td></tr><tr><td>Underutilized RDS</td><td style="text-align:right">6</td><td style="text-align:right">$257.00</td></tr><tr><td>x86 to Graviton (EC2)</td><td style="text-align:right">15</td><td style="text-align:right">$228.99</td></tr><tr><td>ElastiCache, low memory</td><td style="text-align:right">4</td><td style="text-align:right">$214.17</td></tr><tr><td>Previous-generation instances</td><td style="text-align:right">17</td><td style="text-align:right">$171.74</td></tr><tr><td>Underutilized EKS nodes</td><td style="text-align:right">2</td><td style="text-align:right">$158.53</td></tr><tr><td>Aurora I/O-Optimized</td><td style="text-align:right">1</td><td style="text-align:right">$129.86</td></tr><tr><td>Unattached EBS volumes</td><td style="text-align:right">80</td><td style="text-align:right">$106.34</td></tr><tr><td>Route 53 zones with only NS/SOA records</td><td style="text-align:right">37</td><td style="text-align:right">$82.23</td></tr><tr><td>Unused VPC endpoints</td><td style="text-align:right">36</td><td style="text-align:right">$80.00</td></tr><tr><td>Cross-region data transfer</td><td style="text-align:right">11</td><td style="text-align:right">$33.30</td></tr><tr><td>Old EBS snapshots</td><td style="text-align:right">45</td><td style="text-align:right">$13.96</td></tr><tr><td>CloudWatch log retention</td><td style="text-align:right">50</td><td style="text-align:right">$10.96</td></tr><tr><td>Unattached Elastic IPs</td><td style="text-align:right">5</td><td style="text-align:right">$11.10</td></tr><tr><td>gp2 to gp3 EBS</td><td style="text-align:right">7</td><td style="text-align:right">$1.86</td></tr><tr><td>Everything else (hygiene)</td><td style="text-align:right">~30</td><td style="text-align:right">~$0</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-patterns-fall-out-of-this">Two patterns fall out of this<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#two-patterns-fall-out-of-this" class="hash-link" aria-label="Direct link to Two patterns fall out of this" title="Direct link to Two patterns fall out of this" translate="no">​</a></h2>
<p><strong>The money is in commitment and right-sizing, and it overlaps.</strong> The top six lines, Spot,
Savings Plans, idle EC2, ElastiCache, RI coverage, and RDS, are about $2,500/month between them.
They are also the most entangled. The same handful of steady EC2 instances drives the Spot,
Savings Plan, RI, and right-size numbers at once. The real, non-double-counted win here is
smaller than the sum, and it starts with one decision: are these workloads staying? If yes,
commit (Savings Plan). If they are bursty and fault-tolerant, Spot. If they are oversized, shrink
them first, then commit to the smaller footprint. Do not buy a three-year commitment on an
instance you are about to halve.</p>
<p><strong>The volume is in hygiene, and it is nobody's job.</strong> Look at the counts, not the dollars: 80
unattached EBS volumes, 50 log groups with no retention cap, 45 old snapshots, 37 Route 53 zones
holding nothing but default records, 36 VPC endpoints with no traffic. That is 248 findings for a
few hundred dollars. Individually trivial, collectively a mess, and it accumulates because no
single person owns "delete the thing we stopped using." A scan is how you find it, because you
are never going to click through 1,358 resources by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-modernization-layer">The modernization layer<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#the-modernization-layer" class="hash-link" aria-label="Direct link to The modernization layer" title="Direct link to The modernization layer" translate="no">​</a></h2>
<p>Three findings are not really about this month's bill, they are about not overpaying structurally:</p>
<ul>
<li class=""><strong>15 instances on x86 with a Graviton equivalent</strong> ($229/mo here). Graviton is the lowest-effort
price-performance lever AWS offers, roughly 20% cheaper for a family swap, and the current
target is Graviton4 (m8g, c8g, r8g). More on this in the Graviton series.</li>
<li class=""><strong>17 previous-generation instances</strong> ($172/mo). m5, c5, r5 and older, quietly costing more than
current-gen for the same work.</li>
<li class=""><strong>7 gp2 volumes</strong> that should be gp3 ($2/mo here, but it is a free 20% on storage and a
zero-downtime change, so there is no reason not to).</li>
</ul>
<p>Small numbers in this one account, but they are one-time migrations that keep paying, and they
scale with fleet size.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="one-honest-miss-because-it-is-a-good-lesson">One honest miss, because it is a good lesson<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#one-honest-miss-because-it-is-a-good-lesson" class="hash-link" aria-label="Direct link to One honest miss, because it is a good lesson" title="Direct link to One honest miss, because it is a good lesson" translate="no">​</a></h2>
<p>The scan flagged <strong>7 x86 Lambda functions</strong> that could move to arm64 for about 20% off, and
reported <strong>$0.00</strong> in savings for all of them. Not a bug. This account had no per-function cost
data available to the scan, so the rule correctly emitted the recommendation with no dollar
figure rather than inventing one. arm64 Lambda is still worth doing, a one-line architecture
change, but it is a good reminder: a savings estimate is only as good as the cost data behind it,
and a tool that makes up numbers when it has none is not doing you a favor.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-we-would-actually-do-first">What we would actually do first<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#what-we-would-actually-do-first" class="hash-link" aria-label="Direct link to What we would actually do first" title="Direct link to What we would actually do first" translate="no">​</a></h2>
<p>Not all 403. Three moves, in order:</p>
<ol>
<li class=""><strong>Delete the orphans.</strong> Unattached volumes, unattached EIPs, empty Route 53 zones, dead VPC
endpoints. Low risk, no downside, and it clears the noise so the real signal is easier to see.</li>
<li class=""><strong>Right-size, then commit.</strong> Shrink the oversized EC2, RDS, and ElastiCache first. Then put a
Compute Savings Plan over the steady baseline that remains.</li>
<li class=""><strong>Take the free modernizations.</strong> gp2 to gp3, and the Graviton family swaps on anything that is
already ARM-compatible.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="run-it-on-your-own-account">Run it on your own account<a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account#run-it-on-your-own-account" class="hash-link" aria-label="Direct link to Run it on your own account" title="Direct link to Run it on your own account" translate="no">​</a></h2>
<p>Every number above came from one read-only scan. You can run the same 89 checks against your
account with <a href="https://cca.dragonfractal.com/" target="_blank" rel="noopener noreferrer" class="">Cloud Cost Analyzer</a>. The free tier gives you the
executive summary and finding counts, no credit card, and your credentials never leave your
environment. If the surface area looks anything like this account, the ranking alone will tell
you where to point your next afternoon.</p>]]></content:encoded>
            <category>aws</category>
            <category>cost-optimization</category>
            <category>finops</category>
            <category>case-study</category>
        </item>
        <item>
            <title><![CDATA[Graviton in 2026: the migration target moved to m8g, c8g, r8g]]></title>
            <link>https://cca.dragonfractal.com/blog/migrate-to-graviton</link>
            <guid>https://cca.dragonfractal.com/blog/migrate-to-graviton</guid>
            <pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AWS Graviton4 (m8g, c8g, r8g) is the current-generation target, up to 30% faster than Graviton3 and still around 20% cheaper than x86. If your last Graviton pass stopped at m6g or m7g, here is the 2026 update: the family map, how to find candidates, and how to migrate safely.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> AWS Graviton is still about <strong>20% cheaper</strong> than equivalent x86 and up to
<strong>40% better price/performance</strong>, and for most workloads it is a one-line instance-type swap. The
thing that changed in 2026 is the <strong>target</strong>. Graviton4 (<strong>m8g, c8g, r8g</strong>) is now the
current generation, up to 30% faster than Graviton3. If your last migration stopped at m6g or
m7g, this is the update.</p>
<p>This is the anchor post for a short Graviton series. The per-service guides (Lambda, Fargate,
RDS and ElastiCache) build on this one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-changed-graviton4-is-the-default-now">What changed: Graviton4 is the default now<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#what-changed-graviton4-is-the-default-now" class="hash-link" aria-label="Direct link to What changed: Graviton4 is the default now" title="Direct link to What changed: Graviton4 is the default now" translate="no">​</a></h2>
<p>Graviton is AWS's own ARM64 processor, and the ARM families are priced below their x86
counterparts for the same vCPU and memory. That part has not changed. What changed is the
generation you should be targeting. Graviton4 shipped across the general families in 2024, so the
2026 map is:</p>
<table><thead><tr><th>x86 family</th><th>Graviton target (2026)</th></tr></thead><tbody><tr><td>m5 / m6i / m6a</td><td><strong>m8g</strong></td></tr><tr><td>c5 / c6i / c6a</td><td><strong>c8g</strong></td></tr><tr><td>r5 / r6i / r6a</td><td><strong>r8g</strong></td></tr><tr><td>t2 / t3 / t3a</td><td><strong>t4g</strong></td></tr></tbody></table>
<p>Two notes on that table:</p>
<ul>
<li class=""><strong>m8g / c8g / r8g are Graviton4.</strong> If a specific size or region does not have the 8th-gen family
yet, m7g / c7g / r7g (Graviton3) is the fallback, and still a clear win over x86.</li>
<li class=""><strong>Burstable stays on t4g.</strong> AWS has not shipped a Graviton4 T family, so t4g (Graviton2) remains
the ARM target for t2 / t3 / t3a.</li>
</ul>
<p>Graviton also backs the managed services: RDS and Aurora (db.m8g, db.r8g), ElastiCache, and
OpenSearch all offer Graviton classes at the same kind of discount. Those get their own post in
this series.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-find-x86-instances-that-could-move">Step 1: find x86 instances that could move<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#step-1-find-x86-instances-that-could-move" class="hash-link" aria-label="Direct link to Step 1: find x86 instances that could move" title="Direct link to Step 1: find x86 instances that could move" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-instances </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--filters</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">instance-state-name,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">running </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,Arch:Architecture}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Anything with <code>Architecture: x86_64</code> on an m, c, r, or t family is a candidate. When we ran our
full scan across <a class="" href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account">one real account</a>, 15 running instances
had a Graviton equivalent sitting right there.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-migrate-safely">Step 2: migrate safely<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#step-2-migrate-safely" class="hash-link" aria-label="Direct link to Step 2: migrate safely" title="Direct link to Step 2: migrate safely" translate="no">​</a></h2>
<p>The instance change itself is trivial (stop, modify type to the Graviton target, start):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 stop-instances --instance-ids i-0abc123</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 modify-instance-attribute --instance-id i-0abc123 --instance-type m8g.large</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 start-instances --instance-ids i-0abc123</span><br></span></code></pre></div></div>
<p>The real work is making sure your software runs on ARM64. In practice:</p>
<ul>
<li class=""><strong>Interpreted and JIT languages</strong> (Node, Python, Ruby, Go, Java, .NET) run on ARM with no code
change. Just make sure native dependencies have ARM builds.</li>
<li class=""><strong>Containers</strong>: build <strong>multi-arch images</strong> (<code>docker buildx --platform linux/amd64,linux/arm64</code>) and your ECS or EKS tasks run on Graviton unchanged.</li>
<li class=""><strong>AMIs</strong>: use an ARM64 AMI for the new instances.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-caveat-test-before-you-cut-over">The caveat: test before you cut over<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#the-caveat-test-before-you-cut-over" class="hash-link" aria-label="Direct link to The caveat: test before you cut over" title="Direct link to The caveat: test before you cut over" translate="no">​</a></h2>
<p>The one thing that bites: a dependency with <strong>x86-only native code</strong> (an old binary, a
proprietary agent, a compiled extension without an ARM build). Migrate a non-production instance
first, run your test suite, and check any third-party agents (monitoring, security) support ARM.
Once green, roll it out. The savings are permanent and require no ongoing work.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>graviton-migration-opportunities</code> rule flags x86 instances with a Graviton
target and estimates the savings (it only surfaces moves worth at least ~20%), alongside 89 other
cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials never leave it.
<a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-in-the-series">Next in the series<a href="https://cca.dragonfractal.com/blog/migrate-to-graviton#next-in-the-series" class="hash-link" aria-label="Direct link to Next in the series" title="Direct link to Next in the series" translate="no">​</a></h2>
<ul>
<li class="">Move your x86 Lambdas to arm64 in one line</li>
<li class="">Fargate on Graviton: ~20% off containers</li>
<li class="">RDS and ElastiCache on Graviton</li>
<li class="">The full Graviton migration playbook</li>
</ul>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/89-cost-checks-one-aws-account">We ran 89 cost checks on one AWS account: $32,000/year in findings, ranked</a></li><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3">Migrate EBS gp2 to gp3: a free 20% on storage</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws ec2 describe-instances</code></li><li><code>aws ec2 modify-instance-attribute</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>ec2</category>
            <category>graviton</category>
            <category>graviton4</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[The AWS cost optimization checklist: 9 fixes that actually move the bill]]></title>
            <link>https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist</link>
            <guid>https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist</guid>
            <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical, engineer-first checklist for cutting your AWS bill - compute, storage, networking, databases, and logs - with the concrete savings and a deep-dive for each. No procurement meeting required.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> most AWS waste is not a pricing negotiation, it is a pile of
specific resources nobody turned off. This is the checklist we use: find the waste,
rank it by dollars, fix the top items, and stop it from coming back. Each item below
has a concrete number and a step-by-step guide.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-approach-it">How to approach it<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#how-to-approach-it" class="hash-link" aria-label="Direct link to How to approach it" title="Direct link to How to approach it" translate="no">​</a></h2>
<p>AWS Cost Explorer tells you <em>what you spent</em>. It does not walk your account and hand
you a list of things to delete. So the loop that actually lowers the bill is:</p>
<ol>
<li class=""><strong>Find</strong> the idle, orphaned, and over-provisioned resources across every service.</li>
<li class=""><strong>Rank</strong> them by monthly savings, not by how interesting they are to fix.</li>
<li class=""><strong>Fix</strong> the top of the list first - the boring $32/month items add up faster than
one heroic re-architecture.</li>
<li class=""><strong>Prevent regressions</strong> by re-scanning on a schedule or in CI, so the same waste
does not silently rebuild next quarter.</li>
</ol>
<p>The checklist below is grouped by service area. Every item is a real, repeatable fix
with a linked deep-dive.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="compute">Compute<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#compute" class="hash-link" aria-label="Direct link to Compute" title="Direct link to Compute" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Migrate x86 instances to Graviton (ARM).</strong> Graviton families cost <strong>up to
~20% less</strong> than the equivalent x86 for the same vCPU/memory, and often run faster
per dollar. For most modern workloads it is a one-line instance-type swap.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration guide</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="storage">Storage<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#storage" class="hash-link" aria-label="Direct link to Storage" title="Direct link to Storage" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Migrate EBS gp2 volumes to gp3.</strong> gp3 is <strong>~20% cheaper</strong> than gp2 and usually
faster, with an online migration and zero downtime.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3">gp2 to gp3 migration</a></li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Delete old EBS snapshots.</strong> Snapshots cost $0.05/GB-month and accumulate
forever - especially orphaned ones from deleted volumes and deregistered AMIs.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots">Clean up old EBS snapshots</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="networking">Networking<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#networking" class="hash-link" aria-label="Direct link to Networking" title="Direct link to Networking" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Delete idle NAT Gateways.</strong> An idle NAT Gateway bills <strong>~$32/month</strong> plus data
charges for doing nothing. Many can be replaced with free VPC endpoints.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways">Find and delete idle NAT Gateways</a></li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Delete idle load balancers.</strong> An ALB or NLB with no traffic still bills its
<strong>~$16/month</strong> base rate. Look for near-zero requests and empty target groups.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers">Delete idle load balancers</a></li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Release unattached Elastic IPs.</strong> Since 2024 every public IPv4 address costs
money, so an unattached Elastic IP bills <strong>~$3.60/month</strong> for nothing.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips">Stop paying for unattached Elastic IPs</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="databases-and-analytics">Databases and analytics<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#databases-and-analytics" class="hash-link" aria-label="Direct link to Databases and analytics" title="Direct link to Databases and analytics" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Right-size over-provisioned DynamoDB.</strong> Provisioned capacity bills whether you
use it or not. Single-digit utilization means you are overpaying - right-size,
auto-scale, or switch to on-demand.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb">Right-size over-provisioned DynamoDB</a></li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Pause idle Redshift clusters.</strong> A cluster charges per node-hour regardless of
query activity. Idle clusters from old analytics projects are pure waste - pause,
snapshot, or move to Serverless.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters">Idle Redshift clusters bill by the hour</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="observability">Observability<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#observability" class="hash-link" aria-label="Direct link to Observability" title="Direct link to Observability" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Set CloudWatch Logs retention.</strong> Log groups default to <em>infinite</em> retention, so
logs pile up forever at $0.03/GB-month. Set a sane policy on never-expiring groups.
Deep dive: <a class="" href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention">Your CloudWatch Logs never expire</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-this-checklist-does-not-cover">What this checklist does not cover<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#what-this-checklist-does-not-cover" class="hash-link" aria-label="Direct link to What this checklist does not cover" title="Direct link to What this checklist does not cover" translate="no">​</a></h2>
<p>To keep it honest: this list is about waste you can delete or right-size today, which
is where the fastest, lowest-risk savings live. It does <strong>not</strong> cover:</p>
<ul>
<li class=""><strong>Commitments</strong> - Reserved Instances and Savings Plans, which are a separate exercise
once your steady-state usage is clean (buy commitments <em>after</em> you delete waste, not
before, or you will commit to paying for waste).</li>
<li class=""><strong>Architecture</strong> - moving to spot, serverless, or cheaper data-transfer paths, which
are higher-effort structural changes.</li>
</ul>
<p>Clean up the list above first. It is the cheapest money you will ever save.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="automate-the-whole-loop">Automate the whole loop<a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist#automate-the-whole-loop" class="hash-link" aria-label="Direct link to Automate the whole loop" title="Direct link to Automate the whole loop" translate="no">​</a></h2>
<p>Doing this by hand once is worth it. Doing it by hand every quarter is not.
<a href="https://cca.dragonfractal.com/" target="_blank" rel="noopener noreferrer" class="">Cloud Cost Analyzer</a> runs all of these checks (and 80+
more) as a single read-only CLI scan - it finds every item on this list across your
account, ranks them by savings, and you can wire it into CI so new waste fails a build
instead of quietly showing up on next month's invoice. Your credentials never leave
your environment, with an air-gapped option for regulated teams.</p>
<p><a href="https://cca.dragonfractal.com/register" target="_blank" rel="noopener noreferrer" class="">Run your first scan free</a></p>]]></content:encoded>
            <category>aws</category>
            <category>cost-optimization</category>
            <category>finops</category>
            <category>checklist</category>
            <category>guide</category>
        </item>
        <item>
            <title><![CDATA[Case study: cutting $23,000/year from a production AWS bill, verified in Cost Explorer]]></title>
            <link>https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study</link>
            <guid>https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study</guid>
            <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A real production AWS account cut $1,926/month from six targeted fixes, confirmed against AWS Cost Explorer over three billing cycles, with zero production incidents. Here is exactly what moved the bill.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> we scanned a real production AWS account (~$10.7K/month, 1,084 resources
in us-east-1), fixed six things, and cut <strong>$1,925.57/month</strong>. Every dollar was checked against
AWS Cost Explorer billing data over three cycles. No production incidents. Here is what actually
moved the bill.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-account">The account<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#the-account" class="hash-link" aria-label="Direct link to The account" title="Direct link to The account" translate="no">​</a></h2>
<p>A normal mid-size setup: Aurora and DocumentDB databases, EC2 for data pipelines, and the usual
pile of resources nobody had looked at in a year. The scan flagged plenty. What matters is what
happened after we acted on it and watched the invoice.</p>
<p>These are <strong>realized</strong> numbers, not "potential." A tool will happily tell you one instance could
save money three ways: buy a Reserved Instance, turn it off, or right-size it. You get to pick
one. Everything below is the actual run-rate change in Cost Explorer, so it is what the account
stopped paying.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-two-fixes-that-were-92-of-it">The two fixes that were 92% of it<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#the-two-fixes-that-were-92-of-it" class="hash-link" aria-label="Direct link to The two fixes that were 92% of it" title="Direct link to The two fixes that were 92% of it" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="aurora-io-optimized-942month">Aurora I/O-Optimized: $942/month<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#aurora-io-optimized-942month" class="hash-link" aria-label="Direct link to Aurora I/O-Optimized: $942/month" title="Direct link to Aurora I/O-Optimized: $942/month" translate="no">​</a></h3>
<p>The biggest one. The Aurora clusters were on the standard config and paying per-request I/O
charges every month, flat and predictable at $1,218 to $1,245 across the prior quarter.
I/O-Optimized drops the per-I/O charge for a higher flat instance and storage rate:</p>
<table><thead><tr><th>Cost component</th><th>Before</th><th>After</th><th>Delta</th></tr></thead><tbody><tr><td>Per-request I/O charges</td><td>$1,231.97</td><td>$20.65</td><td>-$1,211.32</td></tr><tr><td>I/O-Optimized storage</td><td>$0.00</td><td>$39.14</td><td>+$39.14</td></tr><tr><td>Instance (standard to I/O-Optimized)</td><td>$822.86</td><td>$1,035.35</td><td>+$212.49</td></tr><tr><td>Standard storage</td><td>$24.53</td><td>$7.27</td><td>-$17.26</td></tr><tr><td><strong>Net</strong></td><td></td><td></td><td><strong>-$942.44/mo</strong></td></tr></tbody></table>
<p>We watched the cluster for three days after the switch before calling it done. Nothing moved
in the wrong direction:</p>
<table><thead><tr><th>Metric (3 days post-switch)</th><th>Value</th></tr></thead><tbody><tr><td>Read latency</td><td>0.62ms avg, 3.58ms max</td></tr><tr><td>Write latency</td><td>0.21ms avg, 0.61ms max</td></tr><tr><td>CPU</td><td>12.8% avg, 52.7% max</td></tr><tr><td>Connections</td><td>~51 avg, 118 max (stable)</td></tr><tr><td>RDS events</td><td>none</td></tr></tbody></table>
<p>If your Aurora I/O spend is high and steady, this is about the safest large win there is. The
switch is a config change, not a migration, and it is reversible.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="decommission-idle-documentdb-825month">Decommission idle DocumentDB: $825/month<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#decommission-idle-documentdb-825month" class="hash-link" aria-label="Direct link to Decommission idle DocumentDB: $825/month" title="Direct link to Decommission idle DocumentDB: $825/month" translate="no">​</a></h3>
<p>Four DocumentDB instances across two clusters, sitting at roughly zero connections. The service
that used them had already been moved off DocumentDB months earlier. We confirmed zero live
connections with VPC Flow Log queries, took manual snapshots for rollback, and deleted the
clusters. Cost went from $825.09 to $0.10. These were also end-of-life DocumentDB 3.6 clusters
with a March 30 EOL deadline, so deleting them killed a forced upgrade at the same time.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-other-four">The other four<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#the-other-four" class="hash-link" aria-label="Direct link to The other four" title="Direct link to The other four" translate="no">​</a></h3>
<p>Migrating a data-pipeline instance from x86 to Graviton, stopping an idle dev instance and a
dead SFTP box, and removing unused public-facing infrastructure. Smaller line items, but they
came with wins that were not about cost (below).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-bill-month-by-month">The bill, month by month<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#the-bill-month-by-month" class="hash-link" aria-label="Direct link to The bill, month by month" title="Direct link to The bill, month by month" translate="no">​</a></h2>
<p>The savings landed on the targeted services and held. Cost Explorer, targeted services only:</p>
<table><thead><tr><th>Service</th><th>Jan (baseline)</th><th>Feb</th><th>Mar</th><th>Apr</th><th>Change</th></tr></thead><tbody><tr><td>DocumentDB</td><td>$825.09</td><td>$745.31</td><td>$273.23</td><td>$0.10</td><td>-$824.99</td></tr><tr><td>RDS / Aurora</td><td>$3,054.53</td><td>$2,779.65</td><td>$2,327.70</td><td>$2,039.81</td><td>-$1,014.72</td></tr><tr><td>EC2 compute</td><td>$88.32</td><td>$107.64</td><td>$63.39</td><td>$2.47</td><td>-$85.85</td></tr><tr><td><strong>Total</strong></td><td><strong>$3,967.94</strong></td><td><strong>$3,632.60</strong></td><td><strong>$2,664.32</strong></td><td><strong>$2,042.37</strong></td><td><strong>-$1,925.57</strong></td></tr></tbody></table>
<p>One honest caveat: the <em>whole</em> account bill fell by less than $1,925, because unrelated services
(EKS control plane, AWS Config) grew over the same months. The savings on the targeted resources
are real and isolated. They do not mean the entire bill dropped by the same amount, and any cost
report that pretends otherwise is selling you something.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-it-cost-in-downtime">What it cost in downtime<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#what-it-cost-in-downtime" class="hash-link" aria-label="Direct link to What it cost in downtime" title="Direct link to What it cost in downtime" translate="no">​</a></h2>
<p>Nothing. Zero production incidents. The original projection was $1,900 to $2,300/month; the
realized $1,925.57 landed inside it. We validated the January baseline against the prior quarter
first, to make sure it was a normal month and not a spike we were measuring against.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-the-savings-showed-up">How the savings showed up<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#how-the-savings-showed-up" class="hash-link" aria-label="Direct link to How the savings showed up" title="Direct link to How the savings showed up" translate="no">​</a></h2>
<p>The savings did not land all at once. They ramped as each fix went in, which is exactly what
you want to see: the run rate moving with the actions, not before them. Cumulative savings
against the January baseline:</p>
<table><thead><tr><th>Period</th><th>Realized vs baseline</th><th>What landed</th></tr></thead><tbody><tr><td>February</td><td>$335</td><td>SFTP box and idle dev instance stopped mid-month</td></tr><tr><td>March</td><td>$1,304</td><td>DocumentDB deleted, Aurora I/O switch applied mid-month</td></tr><tr><td>April 1 to 22</td><td>$1,318</td><td>everything fully in effect</td></tr></tbody></table>
<p>By April the monthly run rate had settled at the full $1,925.57. Total realized in the first
three months was $2,957.18 while the fixes were still phasing in.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-wins-that-were-not-about-cost">The wins that were not about cost<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#the-wins-that-were-not-about-cost" class="hash-link" aria-label="Direct link to The wins that were not about cost" title="Direct link to The wins that were not about cost" translate="no">​</a></h2>
<ul>
<li class=""><strong>29 to 62% faster ETL</strong> on Graviton. Nightly pipelines that ran 8 to 18 minutes dropped by a
third to two thirds. See the <a class="" href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration guide</a> for how the
move works.</li>
<li class=""><strong>Killed an EOL deadline.</strong> The DocumentDB 3.6 clusters would have forced an upgrade by March
30. Deleting them made it moot.</li>
<li class=""><strong>Smaller attack surface.</strong> An unused public-facing SFTP host is gone, and the new Graviton
instance enforces IMDSv2.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-we-left-on-the-table">What we left on the table<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#what-we-left-on-the-table" class="hash-link" aria-label="Direct link to What we left on the table" title="Direct link to What we left on the table" translate="no">​</a></h2>
<p>We stopped at the safe, high-value fixes and did not chase every dollar. A few smaller items are
still open on purpose:</p>
<ul>
<li class="">A Reserved Instance or Savings Plan on the new Graviton instance, once its steady-state usage
is confirmed (~$40/mo). Buy commitments after the waste is gone, not before.</li>
<li class="">I/O-Optimized on the remaining dev and QA Aurora clusters (~$20/mo of residual I/O).</li>
<li class="">Terminating two already-stopped instances to drop their lingering EBS charges.</li>
</ul>
<p>None were worth rushing. There is almost always a long tail after the big wins, and it is fine
to leave it for the next quarterly pass.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="find-it-in-your-own-account">Find it in your own account<a href="https://cca.dragonfractal.com/blog/aws-cost-reduction-case-study#find-it-in-your-own-account" class="hash-link" aria-label="Direct link to Find it in your own account" title="Direct link to Find it in your own account" translate="no">​</a></h2>
<p>Every fix here started as a line in a scan. Cloud Cost Analyzer runs the same checks, plus 80+
more, as one read-only pass:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The scan runs in your environment with read-only access, so your credentials never leave it.
Then you do the part that actually saves money: fix the top of the list and check the result in
Cost Explorer. The <a class="" href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">AWS cost optimization checklist</a> walks
through the fixes it looks for.</p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration: ~20% cheaper compute for a one-line instance-type change</a></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>cost-optimization</category>
            <category>finops</category>
            <category>case-study</category>
            <category>aurora</category>
        </item>
        <item>
            <title><![CDATA[Cut your AWS EBS bill ~20% by migrating gp2 to gp3]]></title>
            <link>https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3</link>
            <guid>https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3</guid>
            <pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[gp3 volumes cost about 20% less than gp2 and are usually faster. Here is how to find every gp2 volume and migrate it online with zero downtime - plus the one caveat to check first.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> AWS gp3 EBS volumes cost <strong>~20% less per GB than gp2</strong> ($0.08 vs
$0.10 per GB-month in us-east-1) and include 3,000 IOPS and 125 MB/s of baseline
performance for free. You can convert a volume from gp2 to gp3 <strong>online, with no
downtime and no snapshot</strong>: a single <code>modify-volume</code> call. For most volumes it is
free money. Here is how to find them, migrate them, and the one caveat to check.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-gp3-is-cheaper-and-usually-faster">Why gp3 is cheaper (and usually faster)<a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3#why-gp3-is-cheaper-and-usually-faster" class="hash-link" aria-label="Direct link to Why gp3 is cheaper (and usually faster)" title="Direct link to Why gp3 is cheaper (and usually faster)" translate="no">​</a></h2>
<p>gp2 pricing couples performance to size: you get <strong>3 IOPS per GB</strong>, so the only way
to get more IOPS on gp2 is to over-provision storage you do not need. gp3 decouples
them:</p>
<table><thead><tr><th></th><th>gp2</th><th>gp3</th></tr></thead><tbody><tr><td>Storage</td><td>$0.10 / GB-month</td><td><strong>$0.08 / GB-month</strong> (-20%)</td></tr><tr><td>Baseline IOPS</td><td>3 IOPS/GB (burst to 3,000)</td><td><strong>3,000 included</strong> at any size</td></tr><tr><td>Baseline throughput</td><td>scales with size</td><td><strong>125 MB/s included</strong></td></tr><tr><td>Extra IOPS / throughput</td><td>not possible</td><td>provision independently</td></tr></tbody></table>
<p><em>(Prices are us-east-1 list; the ratio holds across regions.)</em></p>
<p>For a 500 GB volume that is <strong>$50/month to $40/month, i.e. $120/year saved, per volume</strong>,
with equal or better performance. Multiply by every gp2 volume in every account.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---find-your-gp2-volumes">Step 1 - Find your gp2 volumes<a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3#step-1---find-your-gp2-volumes" class="hash-link" aria-label="Direct link to Step 1 - Find your gp2 volumes" title="Direct link to Step 1 - Find your gp2 volumes" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-volumes </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--filters</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">volume-type,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">gp2 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Volumes[].{ID:VolumeId,GiB:Size,AZ:AvailabilityZone,State:State}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Run it per region. EBS is regional, so a volume only shows up in its own region:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">region</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws ec2 describe-regions </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'Regions[].RegionName'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"== </span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c"> =="</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws ec2 describe-volumes </span><span class="token parameter variable" style="color:#36acaa">--region</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--filters</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">volume-type,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">gp2 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Volumes[].{ID:VolumeId,GiB:Size}'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---migrate-online-no-downtime">Step 2 - Migrate (online, no downtime)<a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3#step-2---migrate-online-no-downtime" class="hash-link" aria-label="Direct link to Step 2 - Migrate (online, no downtime)" title="Direct link to Step 2 - Migrate (online, no downtime)" translate="no">​</a></h2>
<p>Converting the type is a live <code>modify-volume</code>. The volume stays attached and
readable/writable the whole time; it briefly enters an <code>optimizing</code> state:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 modify-volume --volume-id vol-0abc123def456 --volume-type gp3</span><br></span></code></pre></div></div>
<p>To convert everything in a region at once:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">vol</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws ec2 describe-volumes </span><span class="token variable punctuation" style="color:#393A34">\</span><span class="token variable" style="color:#36acaa"></span><br></span><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">  </span><span class="token variable parameter variable" style="color:#36acaa">--filters</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable assign-left variable" style="color:#36acaa">Name</span><span class="token variable operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">volume-type,Values</span><span class="token variable operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">gp2 </span><span class="token variable punctuation" style="color:#393A34">\</span><span class="token variable" style="color:#36acaa"></span><br></span><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">  </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'Volumes[].VolumeId'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"migrating </span><span class="token string variable" style="color:#36acaa">$vol</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws ec2 modify-volume --volume-id </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$vol</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> --volume-type gp3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<p>No snapshot, no detach, no reboot. (AWS allows one modification per volume per
6 hours, so migrate, then wait before re-modifying the same volume.)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-one-caveat-high-iops--high-throughput-volumes">The one caveat: high-IOPS / high-throughput volumes<a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3#the-one-caveat-high-iops--high-throughput-volumes" class="hash-link" aria-label="Direct link to The one caveat: high-IOPS / high-throughput volumes" title="Direct link to The one caveat: high-IOPS / high-throughput volumes" translate="no">​</a></h2>
<p>Because gp2 IOPS scale with size, a <strong>large</strong> gp2 volume may already deliver more
than gp3's 3,000 baseline IOPS. A 2 TB gp2 volume provides 6,000 IOPS; if your
workload actually uses them, migrate <strong>and</strong> provision matching performance on gp3:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 modify-volume --volume-id vol-0abc123def456 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --volume-type gp3 </span><span class="token parameter variable" style="color:#36acaa">--iops</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6000</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--throughput</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">250</span><br></span></code></pre></div></div>
<p>The first 3,000 IOPS and 125 MB/s are free; beyond that, extra IOPS are
$0.005/provisioned-IOPS-month and extra throughput $0.040/MB/s-month. Even fully
matched, gp3 is normally still cheaper than the equivalent gp2, but check
CloudWatch <code>VolumeReadOps</code>/<code>VolumeWriteOps</code> before assuming you need the headroom.
Volumes ≤ ~1 TB with normal workloads are a straight 20% win.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Finding gp2 volumes and estimating the savings across every account and region by
hand does not scale. That is exactly what Cloud Cost Analyzer's
<code>ebs-gp2-to-gp3-migration</code> rule does: it flags every gp2 volume and estimates the
monthly saving, alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots">Old EBS snapshots: the backup pile quietly inflating your bill</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration: ~20% cheaper compute for a one-line instance-type change</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws ec2 modify-volume</code></li><li><code>aws ec2 describe-volumes</code></li><li><code>aws ec2 create-snapshot</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>ebs</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Idle load balancers: the ~$16/month each you forgot to delete]]></title>
            <link>https://cca.dragonfractal.com/blog/delete-idle-load-balancers</link>
            <guid>https://cca.dragonfractal.com/blog/delete-idle-load-balancers</guid>
            <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[An idle ALB or NLB still bills its hourly base rate, about $16/month, with no traffic flowing. Here is how to find load balancers with near-zero requests and empty target groups, and delete them safely.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> An Application or Network Load Balancer costs <strong>~$0.0225/hour -
about $16/month - just to exist</strong>, plus capacity units. Classic Load Balancers run
~$18/month. Load balancers outlive the services behind them: the app gets torn down,
the ALB keeps billing. Here is how to find load balancers with no real traffic or no
healthy targets, and remove them safely.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-idle-load-balancers-linger">Why idle load balancers linger<a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers#why-idle-load-balancers-linger" class="hash-link" aria-label="Direct link to Why idle load balancers linger" title="Direct link to Why idle load balancers linger" translate="no">​</a></h2>
<p>The hourly base charge is fixed - an ALB with zero requests bills the same ~$16/month
as a busy one. Load balancers are usually created early (with an app or an IaC
module) and deleted last, if ever. A handful of abandoned ALBs from old
environments is real, recurring money.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---list-load-balancers-and-their-traffic">Step 1 - List load balancers and their traffic<a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers#step-1---list-load-balancers-and-their-traffic" class="hash-link" aria-label="Direct link to Step 1 - List load balancers and their traffic" title="Direct link to Step 1 - List load balancers and their traffic" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws elbv2 describe-load-balancers </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'LoadBalancers[].{Name:LoadBalancerName,Type:Type,ARN:LoadBalancerArn}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>For an ALB, check request volume over the last 7 days (the metric dimension is the
tail of the ARN, e.g. <code>app/my-alb/50dc6c495c0c9188</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws cloudwatch get-metric-statistics </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--namespace</span><span class="token plain"> AWS/ApplicationELB </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --metric-name RequestCount </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--dimensions</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">LoadBalancer,Value</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">app/my-alb/50dc6c495c0c9188 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --start-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-d</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable string" style="color:#e3116c">'7 days ago'</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --end-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--period</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">86400</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--statistics</span><span class="token plain"> Sum </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Datapoints[].Sum'</span><br></span></code></pre></div></div>
<p>Near-zero request counts over a week is a strong idle signal. (For NLBs, use the
<code>AWS/NetworkELB</code> namespace and <code>ActiveFlowCount</code>.)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---check-for-empty-or-unhealthy-target-groups">Step 2 - Check for empty or unhealthy target groups<a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers#step-2---check-for-empty-or-unhealthy-target-groups" class="hash-link" aria-label="Direct link to Step 2 - Check for empty or unhealthy target groups" title="Direct link to Step 2 - Check for empty or unhealthy target groups" translate="no">​</a></h2>
<p>A load balancer with no healthy targets is doing nothing useful:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">tg</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws elbv2 describe-target-groups </span><span class="token variable punctuation" style="color:#393A34">\</span><span class="token variable" style="color:#36acaa"></span><br></span><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">  --load-balancer-arn </span><span class="token variable operator" style="color:#393A34">&lt;</span><span class="token variable" style="color:#36acaa">lb-arn</span><span class="token variable operator" style="color:#393A34">&gt;</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable punctuation" style="color:#393A34">\</span><span class="token variable" style="color:#36acaa"></span><br></span><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">  </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'TargetGroups[].TargetGroupArn'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"== </span><span class="token string variable" style="color:#36acaa">$tg</span><span class="token string" style="color:#e3116c"> =="</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws elbv2 describe-target-health --target-group-arn </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$tg</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'TargetHealthDescriptions[].TargetHealth.State'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<p>Empty output (no targets) or all <code>unhealthy</code> alongside near-zero requests is a
confident "delete me."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3---delete-safely">Step 3 - Delete safely<a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers#step-3---delete-safely" class="hash-link" aria-label="Direct link to Step 3 - Delete safely" title="Direct link to Step 3 - Delete safely" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws elbv2 delete-load-balancer --load-balancer-arn </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">lb-arn</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre></div></div>
<p><strong>Caveat:</strong> a load balancer with no requests is not <em>always</em> dead - it might be a
disaster-recovery endpoint, a rarely-hit admin panel, or the target of a DNS record
that something depends on. Before deleting, check Route 53 (and any external DNS) for
records pointing at the load balancer's DNS name, and confirm nothing references it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws elbv2 describe-load-balancers --load-balancer-arn </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">lb-arn</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'LoadBalancers[].DNSName'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span></code></pre></div></div>
<p>Then grep your DNS zones for that name. No references + no traffic + no healthy
targets = safe to remove.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>idle-load-balancer</code> rule flags load balancers serving fewer
than 100 requests/day over a 7-day window, so you are not manually pulling CloudWatch
for each one - alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways">The $32/month surprise: finding and deleting idle NAT Gateways</a></li><li><a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips">Stop paying for unattached Elastic IPs and orphaned network interfaces</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws elbv2 describe-load-balancers</code></li><li><code>aws elbv2 delete-load-balancer</code></li><li><code>aws ec2 describe-target-group-health</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>networking</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[The $32/month surprise: finding and deleting idle NAT Gateways]]></title>
            <link>https://cca.dragonfractal.com/blog/delete-idle-nat-gateways</link>
            <guid>https://cca.dragonfractal.com/blog/delete-idle-nat-gateways</guid>
            <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[An idle NAT Gateway bills ~$32/month plus data charges for doing nothing. Here is how to find the ones with no real traffic, delete them safely, and cut future NAT costs with free VPC endpoints.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> Every NAT Gateway costs <strong>$0.045/hour, about $32/month, before
a single byte of data</strong>, plus $0.045 per GB processed. They are easy to create per
subnet and easy to forget. Here is how to find NAT Gateways that carry little or no
traffic, delete them without breaking egress, and stop paying NAT data charges for
traffic that could use free VPC endpoints instead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-nat-gateways-get-expensive-quietly">Why NAT Gateways get expensive quietly<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#why-nat-gateways-get-expensive-quietly" class="hash-link" aria-label="Direct link to Why NAT Gateways get expensive quietly" title="Direct link to Why NAT Gateways get expensive quietly" translate="no">​</a></h2>
<p>A NAT Gateway has two costs (us-east-1):</p>
<ul>
<li class=""><strong>Hourly:</strong> $0.045/hour ≈ <strong>$32/month, flat</strong>, whether it moves 0 bytes or 0 GB.</li>
<li class=""><strong>Data processing:</strong> $0.045 per GB that passes through it.</li>
</ul>
<p>The trap is the hourly charge. A common HA pattern puts one NAT Gateway in each
Availability Zone - three AZs is ~<strong>$96/month</strong> in fixed cost. Leftovers from
deleted environments, or NATs in subnets that no longer send traffic, bill the full
$32/month indefinitely.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---list-your-nat-gateways">Step 1 - List your NAT Gateways<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#step-1---list-your-nat-gateways" class="hash-link" aria-label="Direct link to Step 1 - List your NAT Gateways" title="Direct link to Step 1 - List your NAT Gateways" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-nat-gateways </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--filter</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">state,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">available </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'NatGateways[].{ID:NatGatewayId,VPC:VpcId,Subnet:SubnetId}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Across all regions:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">region</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws ec2 describe-regions </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'Regions[].RegionName'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"== </span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c"> =="</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws ec2 describe-nat-gateways </span><span class="token parameter variable" style="color:#36acaa">--region</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--filter</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">state,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">available </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'NatGateways[].NatGatewayId'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---check-whether-each-one-is-actually-used">Step 2 - Check whether each one is actually used<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#step-2---check-whether-each-one-is-actually-used" class="hash-link" aria-label="Direct link to Step 2 - Check whether each one is actually used" title="Direct link to Step 2 - Check whether each one is actually used" translate="no">​</a></h2>
<p>Idle means little to no traffic. Pull the last 7 days of bytes from CloudWatch for a
given NAT Gateway:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws cloudwatch get-metric-statistics </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--namespace</span><span class="token plain"> AWS/NATGateway </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --metric-name BytesOutToDestination </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--dimensions</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">NatGatewayId,Value</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">nat-0abc123def456 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --start-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-d</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable string" style="color:#e3116c">'7 days ago'</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --end-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--period</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">86400</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--statistics</span><span class="token plain"> Sum </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Datapoints[].Sum'</span><br></span></code></pre></div></div>
<p>Near-zero sums over a week means nothing is using it. Also confirm no route table
still points <code>0.0.0.0/0</code> at it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-route-tables </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"RouteTables[?Routes[?NatGatewayId=='nat-0abc123def456']].RouteTableId"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3---delete-it-safely">Step 3 - Delete it safely<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#step-3---delete-it-safely" class="hash-link" aria-label="Direct link to Step 3 - Delete it safely" title="Direct link to Step 3 - Delete it safely" translate="no">​</a></h2>
<p>If no route table depends on it and traffic is flat, delete it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 delete-nat-gateway --nat-gateway-id nat-0abc123def456</span><br></span></code></pre></div></div>
<p>Then release the Elastic IP it held (that is billable too - see the
<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips" target="_blank" rel="noopener noreferrer" class="">unattached Elastic IP post</a>).</p>
<p><strong>Caveat:</strong> a NAT Gateway provides outbound internet for private subnets. If a
workload in that subnet needs egress (package installs, external APIs, updates),
deleting the NAT breaks it. Verify traffic is genuinely flat and no active route
depends on it before deleting. In multi-AZ setups, deleting one AZ's NAT sends that
AZ's traffic cross-AZ (added data cost) or breaks it - decide deliberately.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cut-nat-data-charges-for-free">Cut NAT data charges for free<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#cut-nat-data-charges-for-free" class="hash-link" aria-label="Direct link to Cut NAT data charges for free" title="Direct link to Cut NAT data charges for free" translate="no">​</a></h2>
<p>Traffic to <strong>S3 and DynamoDB</strong> does not need a NAT Gateway at all. A <strong>VPC gateway
endpoint</strong> routes it privately, for <strong>free</strong>, and removes those bytes from your NAT
data-processing bill:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 create-vpc-endpoint </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --vpc-id vpc-0abc123 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --service-name com.amazonaws.us-east-1.s3 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --route-table-ids rtb-0abc123</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Checking NAT traffic across every VPC, subnet, and region by hand does not scale.
Cloud Cost Analyzer's <code>idle-nat-gateway</code> rule flags any NAT Gateway moving less than
1 GB/day over a 7-day window, alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers">Idle load balancers: the ~$16/month each you forgot to delete</a></li><li><a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips">Stop paying for unattached Elastic IPs and orphaned network interfaces</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws ec2 describe-nat-gateways</code></li><li><code>aws ec2 delete-nat-gateway</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>networking</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Stop paying for unattached Elastic IPs and orphaned network interfaces]]></title>
            <link>https://cca.dragonfractal.com/blog/find-unattached-elastic-ips</link>
            <guid>https://cca.dragonfractal.com/blog/find-unattached-elastic-ips</guid>
            <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Since 2024 every public IPv4 address costs money - and an unattached Elastic IP bills ~$3.60/month for nothing. Here is how to find and release orphaned Elastic IPs and network interfaces safely.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> Since <strong>February 2024, AWS charges $0.005/hour for every public
IPv4 address</strong>, about <strong>$3.60/month each</strong>, whether it is attached to anything or
not. An unattached Elastic IP (EIP) is pure waste, and orphaned network interfaces
(ENIs) often hold them. Individually small; at scale, and multiplied across
accounts, it adds up. Here is how to find and release them safely.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-line-item-appeared">Why this line item appeared<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips#why-this-line-item-appeared" class="hash-link" aria-label="Direct link to Why this line item appeared" title="Direct link to Why this line item appeared" translate="no">​</a></h2>
<p>AWS used to give you one free public IPv4 per running instance and only charged for
<strong>idle</strong> Elastic IPs. As of <strong>1 February 2024</strong>, <em>all</em> public IPv4 addresses cost
<strong>$0.005/hour (~$3.60/month)</strong>. Attached ones you are presumably using - but an
<strong>unattached</strong> EIP is billing you for an address doing nothing. Orphaned ENIs left
behind by deleted Lambdas, load balancers, or instances frequently hold these.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---find-unattached-elastic-ips">Step 1 - Find unattached Elastic IPs<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips#step-1---find-unattached-elastic-ips" class="hash-link" aria-label="Direct link to Step 1 - Find unattached Elastic IPs" title="Direct link to Step 1 - Find unattached Elastic IPs" translate="no">​</a></h2>
<p>An EIP with no <code>AssociationId</code> is not attached to anything:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-addresses </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Addresses[?AssociationId==`null`].{IP:PublicIp,AllocId:AllocationId}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Across all regions:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">region</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws ec2 describe-regions </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'Regions[].RegionName'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"== </span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c"> =="</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws ec2 describe-addresses </span><span class="token parameter variable" style="color:#36acaa">--region</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$region</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Addresses[?AssociationId==`null`].PublicIp'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---find-orphaned-network-interfaces">Step 2 - Find orphaned network interfaces<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips#step-2---find-orphaned-network-interfaces" class="hash-link" aria-label="Direct link to Step 2 - Find orphaned network interfaces" title="Direct link to Step 2 - Find orphaned network interfaces" translate="no">​</a></h2>
<p>ENIs in the <code>available</code> state are detached and just sitting there:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-network-interfaces </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--filters</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">status,Values</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">available </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'NetworkInterfaces[].{ENI:NetworkInterfaceId,AZ:AvailabilityZone,Desc:Description}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Beyond cost, orphaned ENIs commonly <strong>block security group deletion</strong> ("resource in
use"), so clearing them untangles cleanup too.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3---release--delete-safely">Step 3 - Release / delete safely<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips#step-3---release--delete-safely" class="hash-link" aria-label="Direct link to Step 3 - Release / delete safely" title="Direct link to Step 3 - Release / delete safely" translate="no">​</a></h2>
<p>Release an unattached EIP:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 release-address --allocation-id eipalloc-0abc123def456</span><br></span></code></pre></div></div>
<p>Delete an available ENI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 delete-network-interface --network-interface-id eni-0abc123def456</span><br></span></code></pre></div></div>
<p><strong>Caveat - the one that bites people:</strong> releasing an Elastic IP <strong>gives up that IP
address permanently</strong>. You will almost certainly get a different one next time. If
the IP is allow-listed in a partner's firewall, hardcoded in DNS, or referenced by a
third party, releasing it breaks that integration. Confirm the address is not
referenced anywhere before releasing. Deleting a detached ENI is low-risk (it is
already attached to nothing), but double-check the description to be sure it is not a
reserved interface for a service you are about to launch.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/find-unattached-elastic-ips#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>unattached-network-interface</code> rule flags detached ENIs and
tells you which ones are holding a billable Elastic IP, so you can clear both in one
pass - alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/delete-idle-load-balancers">Idle load balancers: the ~$16/month each you forgot to delete</a></li><li><a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways">The $32/month surprise: finding and deleting idle NAT Gateways</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws ec2 describe-addresses</code></li><li><code>aws ec2 release-address</code></li><li><code>aws ec2 describe-network-interfaces</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>networking</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Old EBS snapshots: the backup pile quietly inflating your bill]]></title>
            <link>https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots</link>
            <guid>https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots</guid>
            <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[EBS snapshots cost $0.05/GB-month and accumulate forever - old ones from deleted volumes and AMIs are rarely cleaned up. Here is how to find, archive, and delete them safely.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> EBS snapshots cost <strong>$0.05/GB-month</strong> and nobody ever deletes
them. Backups from volumes and AMIs that were removed years ago keep billing. For
snapshots you must retain but rarely touch, the <strong>archive tier is ~75% cheaper</strong>
($0.0125/GB-month). Here is how to find the pile, delete what is dead, and archive
what is not.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-snapshots-pile-up">Why snapshots pile up<a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots#why-snapshots-pile-up" class="hash-link" aria-label="Direct link to Why snapshots pile up" title="Direct link to Why snapshots pile up" translate="no">​</a></h2>
<p>Snapshots are incremental, so the first one is full-size and later ones only store
changed blocks - which makes them feel cheap. But they never expire on their own,
and automation (AMI bakes, backup jobs, <code>create-image</code>) generates them constantly.
Delete the volume or deregister the AMI and the underlying snapshots often stay,
billing indefinitely. A multi-year account routinely has thousands.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---find-old-snapshots-you-own">Step 1 - Find old snapshots you own<a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots#step-1---find-old-snapshots-you-own" class="hash-link" aria-label="Direct link to Step 1 - Find old snapshots you own" title="Direct link to Step 1 - Find old snapshots you own" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-snapshots --owner-ids self </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'sort_by(Snapshots, &amp;StartTime)[].{ID:SnapshotId,GiB:VolumeSize,Started:StartTime,Desc:Description}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Just the ones older than 90 days:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">cutoff</span><span class="token operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable function" style="color:#d73a49">date</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">-u</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">-d</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'90 days ago'</span><span class="token variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-snapshots --owner-ids self </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Snapshots[?StartTime&lt;='</span><span class="token string variable" style="color:#36acaa">${cutoff}</span><span class="token string" style="color:#e3116c">'].{ID:SnapshotId,GiB:VolumeSize,Started:StartTime}"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---check-before-you-delete">Step 2 - Check before you delete<a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots#step-2---check-before-you-delete" class="hash-link" aria-label="Direct link to Step 2 - Check before you delete" title="Direct link to Step 2 - Check before you delete" translate="no">​</a></h2>
<p>The critical check: <strong>is a snapshot backing an AMI?</strong> Deleting one that is breaks the
AMI. List snapshots referenced by your AMIs:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 describe-images </span><span class="token parameter variable" style="color:#36acaa">--owners</span><span class="token plain"> self </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Images[].BlockDeviceMappings[].Ebs.SnapshotId'</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> text </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tr</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'\t'</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'\n'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-u</span><br></span></code></pre></div></div>
<p>Anything in that list is in use - leave it (or deregister the AMI first if the AMI
itself is obsolete).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3---delete-or-archive">Step 3 - Delete or archive<a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots#step-3---delete-or-archive" class="hash-link" aria-label="Direct link to Step 3 - Delete or archive" title="Direct link to Step 3 - Delete or archive" translate="no">​</a></h2>
<p>Delete a truly dead snapshot:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 delete-snapshot --snapshot-id snap-0abc123def456</span><br></span></code></pre></div></div>
<p>For compliance/DR snapshots you must keep but rarely restore, move them to the
<strong>archive tier</strong> instead of deleting:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws ec2 modify-snapshot-tier --snapshot-id snap-0abc123def456 --storage-tier archive</span><br></span></code></pre></div></div>
<p><strong>Caveats:</strong> deleting an <em>incremental</em> snapshot is safe - AWS re-parents the blocks
later snapshots need, so you never corrupt a chain. The real risk is AMIs (above).
Archive has a <strong>90-day minimum</strong> and restore takes 24-72h plus a retrieval fee, so
only archive things you genuinely will not need quickly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>ebs-snapshot-archive</code> rule surfaces old, large, rarely
accessed snapshots and estimates the archive/delete savings - alongside 89 other
cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-ebs-gp2-to-gp3">Cut your AWS EBS bill ~20% by migrating gp2 to gp3</a></li><li><a href="https://cca.dragonfractal.com/blog/delete-idle-nat-gateways">The $32/month surprise: finding and deleting idle NAT Gateways</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws ec2 describe-snapshots</code></li><li><code>aws ec2 delete-snapshot</code></li><li><code>aws ec2 describe-images</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>ebs</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Idle Redshift clusters bill by the hour - even when nobody queries them]]></title>
            <link>https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters</link>
            <guid>https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters</guid>
            <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A Redshift cluster charges per node-hour regardless of query activity. Idle clusters from old analytics projects are expensive. Here is how to find them and pause, snapshot, or move to Serverless.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> Amazon Redshift bills <strong>per node-hour</strong> whether the cluster runs a
million queries or zero. A multi-node cluster left over from an old analytics project
can run <strong>hundreds of dollars a month</strong> for nothing. Here is how to find idle
clusters and stop the bleeding - pause, snapshot-and-delete, or move to Serverless.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-idle-redshift-is-expensive">Why idle Redshift is expensive<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#why-idle-redshift-is-expensive" class="hash-link" aria-label="Direct link to Why idle Redshift is expensive" title="Direct link to Why idle Redshift is expensive" translate="no">​</a></h2>
<p>Provisioned Redshift charges for compute by node, continuously. An <code>ra3.xlplus</code> node
is <del>$1.086/hour; a modest 2-node cluster is **</del>$1,560/month** even if it is queried
once a week. Analytics clusters are prime candidates to go idle - a dashboard project
ships, the team moves on, and the cluster keeps running.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---find-your-clusters">Step 1 - Find your clusters<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#step-1---find-your-clusters" class="hash-link" aria-label="Direct link to Step 1 - Find your clusters" title="Direct link to Step 1 - Find your clusters" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws redshift describe-clusters </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Clusters[].{ID:ClusterIdentifier,Node:NodeType,Count:NumberOfNodes,Status:ClusterStatus}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---check-query-activity">Step 2 - Check query activity<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#step-2---check-query-activity" class="hash-link" aria-label="Direct link to Step 2 - Check query activity" title="Direct link to Step 2 - Check query activity" translate="no">​</a></h2>
<p>Idle means almost no queries. Pull the last 7 days of query volume from CloudWatch:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws cloudwatch get-metric-statistics </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--namespace</span><span class="token plain"> AWS/Redshift --metric-name QueriesCompletedPerSecond </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--dimensions</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ClusterIdentifier,Value</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">my-cluster </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --start-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-d</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable string" style="color:#e3116c">'7 days ago'</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --end-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--period</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">86400</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--statistics</span><span class="token plain"> Sum </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Datapoints[].Sum'</span><br></span></code></pre></div></div>
<p>Near-zero over a week is a clear idle signal.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3---pick-a-fix">Step 3 - Pick a fix<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#step-3---pick-a-fix" class="hash-link" aria-label="Direct link to Step 3 - Pick a fix" title="Direct link to Step 3 - Pick a fix" translate="no">​</a></h2>
<ul>
<li class=""><strong>Used occasionally</strong> -&gt; <strong>pause</strong> it. Paused clusters stop compute billing (you
still pay for storage) and resume in minutes:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws redshift pause-cluster --cluster-identifier my-cluster</span><br></span></code></pre></div></div>
</li>
<li class=""><strong>Not needed, but keep the data</strong> -&gt; <strong>snapshot and delete</strong>. You can restore later
from the snapshot:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws redshift delete-cluster --cluster-identifier my-cluster </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --final-cluster-snapshot-identifier my-cluster-final</span><br></span></code></pre></div></div>
</li>
<li class=""><strong>Sporadic, unpredictable use</strong> -&gt; move to <strong>Redshift Serverless</strong>, which bills for
compute only while queries run.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-caveat-know-what-depends-on-it">The caveat: know what depends on it<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#the-caveat-know-what-depends-on-it" class="hash-link" aria-label="Direct link to The caveat: know what depends on it" title="Direct link to The caveat: know what depends on it" translate="no">​</a></h2>
<p>Before pausing or deleting, check what talks to the cluster - scheduled ETL jobs, BI
tools (QuickSight, Tableau), and downstream dashboards will fail against a paused or
deleted cluster. Pausing is reversible and low-risk; deleting requires the final
snapshot (always take it) and confirmation that no automated job expects the cluster
to be live.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>idle-redshift</code> rule flags clusters running fewer than ~10
queries/day so you can pause or retire them - alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb">Over-provisioned DynamoDB: paying for capacity you never use</a></li><li><a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention">Your CloudWatch Logs never expire - and that is a growing bill</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws redshift describe-cluster</code></li><li><code>aws redshift pause-cluster</code></li><li><code>aws redshift create-cluster-snapshot</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>redshift</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Over-provisioned DynamoDB: paying for capacity you never use]]></title>
            <link>https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb</link>
            <guid>https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb</guid>
            <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Provisioned DynamoDB capacity bills whether you use it or not. If your tables run at single-digit utilization, you are overpaying. Here is how to spot it and fix it - right-size, auto-scale, or switch to on-demand.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> With <strong>provisioned</strong> DynamoDB capacity you pay for the read/write
units you reserve, <strong>used or not</strong>. Tables are routinely over-provisioned "to be
safe" and then run at 5-10% utilization for years. Here is how to find them and pick
the right fix: right-size, turn on auto-scaling, or move to on-demand.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-over-provisioning-happens">Why over-provisioning happens<a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb#why-over-provisioning-happens" class="hash-link" aria-label="Direct link to Why over-provisioning happens" title="Direct link to Why over-provisioning happens" translate="no">​</a></h2>
<p>Someone sets a table to 1,000 RCU / 1,000 WCU during a launch or load test, traffic
never materializes, and the provisioned numbers are never revisited. You keep paying
for 1,000 units while consuming 50. Because DynamoDB does not bill a big scary line
item, it hides.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---compare-provisioned-vs-consumed">Step 1 - Compare provisioned vs consumed<a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb#step-1---compare-provisioned-vs-consumed" class="hash-link" aria-label="Direct link to Step 1 - Compare provisioned vs consumed" title="Direct link to Step 1 - Compare provisioned vs consumed" translate="no">​</a></h2>
<p>Check a table's provisioned capacity:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws dynamodb describe-table --table-name my-table </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Table.ProvisionedThroughput.{Read:ReadCapacityUnits,Write:WriteCapacityUnits}'</span><br></span></code></pre></div></div>
<p>Then pull actual consumption over the last week from CloudWatch:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws cloudwatch get-metric-statistics </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--namespace</span><span class="token plain"> AWS/DynamoDB --metric-name ConsumedReadCapacityUnits </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--dimensions</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">Name</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">TableName,Value</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">my-table </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --start-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-d</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable string" style="color:#e3116c">'7 days ago'</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --end-time </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">date</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa"> +%Y-%m-%dT%H:%M:%SZ</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--period</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">86400</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--statistics</span><span class="token plain"> Average Maximum </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Datapoints[].{Avg:Average,Max:Maximum}'</span><br></span></code></pre></div></div>
<p>Average consumption far below provisioned (say, under 20%) means you are overpaying.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---pick-the-right-fix">Step 2 - Pick the right fix<a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb#step-2---pick-the-right-fix" class="hash-link" aria-label="Direct link to Step 2 - Pick the right fix" title="Direct link to Step 2 - Pick the right fix" translate="no">​</a></h2>
<ul>
<li class=""><strong>Steady, predictable traffic that is just set too high</strong> -&gt; lower the provisioned
numbers to match reality:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws dynamodb update-table --table-name my-table </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --provisioned-throughput </span><span class="token assign-left variable" style="color:#36acaa">ReadCapacityUnits</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100</span><span class="token plain">,WriteCapacityUnits</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100</span><br></span></code></pre></div></div>
</li>
<li class=""><strong>Variable-but-known daily pattern</strong> -&gt; enable <strong>auto-scaling</strong> so capacity tracks
demand instead of sitting at a fixed ceiling.</li>
<li class=""><strong>Spiky or unpredictable, low-average traffic</strong> -&gt; switch to <strong>on-demand</strong>, which
bills per request with no reserved capacity:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws dynamodb update-table --table-name my-table --billing-mode PAY_PER_REQUEST</span><br></span></code></pre></div></div>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-caveat-on-demand-is-not-always-cheaper">The caveat: on-demand is not always cheaper<a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb#the-caveat-on-demand-is-not-always-cheaper" class="hash-link" aria-label="Direct link to The caveat: on-demand is not always cheaper" title="Direct link to The caveat: on-demand is not always cheaper" translate="no">​</a></h2>
<p>On-demand costs roughly <strong>5-7x more per request</strong> than provisioned. It wins for
spiky, low-average, or unpredictable workloads (you pay nothing when idle), but for
<strong>steady high throughput</strong> provisioned + auto-scaling is far cheaper. Right-size
based on the consumption pattern, not a blanket switch. You can change billing mode
once per 24 hours, so decide deliberately.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/right-size-over-provisioned-dynamodb#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>dynamodb-over-provisioned</code> rule flags tables running below 20%
utilization and estimates the right-sizing savings - alongside 89 other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/pause-idle-redshift-clusters">Idle Redshift clusters bill by the hour - even when nobody queries them</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration: ~20% cheaper compute for a one-line instance-type change</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws dynamodb describe-table</code></li><li><code>aws dynamodb update-table</code></li><li><code>aws dynamodb describe-auto-scaling-settings</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>dynamodb</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Your CloudWatch Logs never expire - and that is a growing bill]]></title>
            <link>https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention</link>
            <guid>https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention</guid>
            <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[CloudWatch log groups default to infinite retention, so logs accumulate forever at $0.03/GB-month. Here is how to find never-expiring log groups and set a sane retention policy.]]></description>
            <content:encoded><![CDATA[<p><strong>Short version:</strong> By default, a CloudWatch log group keeps logs <strong>forever</strong>. Stored
logs cost <strong>$0.03/GB-month</strong>, and every Lambda, ECS task, and API Gateway you have
been running for years has been piling them up. Setting a retention policy is a
one-line fix per log group. Here is how to find the never-expiring ones and cap them.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-quietly-grows">Why this quietly grows<a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention#why-this-quietly-grows" class="hash-link" aria-label="Direct link to Why this quietly grows" title="Direct link to Why this quietly grows" translate="no">​</a></h2>
<p>Two things bill on CloudWatch Logs: <strong>ingestion</strong> ($0.50/GB, one-time) and
<strong>storage</strong> ($0.03/GB-month, forever). New log groups are created with
<code>retentionInDays</code> unset, meaning <strong>never expire</strong>, so storage grows without bound.
Nobody notices because it is a slowly rising line, not a spike. Debug logs from 2022
are still costing you money today.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1---find-log-groups-with-no-retention-set">Step 1 - Find log groups with no retention set<a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention#step-1---find-log-groups-with-no-retention-set" class="hash-link" aria-label="Direct link to Step 1 - Find log groups with no retention set" title="Direct link to Step 1 - Find log groups with no retention set" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws logs describe-log-groups </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'logGroups[?retentionInDays==`null`].{Name:logGroupName,Bytes:storedBytes}'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<p>Sort by size to find the worst offenders first:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws logs describe-log-groups </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--query</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'reverse(sort_by(logGroups[?retentionInDays==`null`], &amp;storedBytes))[:20].[logGroupName,storedBytes]'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--output</span><span class="token plain"> table</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2---set-a-retention-policy">Step 2 - Set a retention policy<a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention#step-2---set-a-retention-policy" class="hash-link" aria-label="Direct link to Step 2 - Set a retention policy" title="Direct link to Step 2 - Set a retention policy" translate="no">​</a></h2>
<p>Pick a sane default (30/90/365 days depending on the log's purpose) and apply it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws logs put-retention-policy </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --log-group-name /aws/lambda/my-function </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --retention-in-days </span><span class="token number" style="color:#36acaa">90</span><br></span></code></pre></div></div>
<p>Apply 90 days to every never-expiring group at once:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#36acaa">lg</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">aws logs describe-log-groups </span><span class="token variable punctuation" style="color:#393A34">\</span><span class="token variable" style="color:#36acaa"></span><br></span><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">  </span><span class="token variable parameter variable" style="color:#36acaa">--query</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'logGroups[?retentionInDays==`null`].logGroupName'</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable parameter variable" style="color:#36acaa">--output</span><span class="token variable" style="color:#36acaa"> text</span><span class="token variable" style="color:#36acaa">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"setting 90d on </span><span class="token string variable" style="color:#36acaa">$lg</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  aws logs put-retention-policy --log-group-name </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$lg</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> --retention-in-days </span><span class="token number" style="color:#36acaa">90</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">done</span><br></span></code></pre></div></div>
<p>Retention applies going forward and prunes existing logs older than the window, so
storage drops on its own after you set it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-caveat-audit-and-compliance-logs">The caveat: audit and compliance logs<a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention#the-caveat-audit-and-compliance-logs" class="hash-link" aria-label="Direct link to The caveat: audit and compliance logs" title="Direct link to The caveat: audit and compliance logs" translate="no">​</a></h2>
<p>Do <strong>not</strong> blanket-expire everything. CloudTrail, VPC Flow Logs, and anything under a
compliance regime (PCI, HIPAA, SOC 2) may have a required minimum retention. For
those, keep a longer window - or better, <strong>export to S3</strong> (or S3 Glacier) for
long-term retention at a fraction of CloudWatch's storage price, then expire the
CloudWatch copy. Separate "I might debug this" logs (short retention) from "I must
retain this" logs (export + policy).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-it-automatically">Do it automatically<a href="https://cca.dragonfractal.com/blog/set-cloudwatch-logs-retention#do-it-automatically" class="hash-link" aria-label="Direct link to Do it automatically" title="Direct link to Do it automatically" translate="no">​</a></h2>
<p>Cloud Cost Analyzer's <code>cloudwatch-logs-retention</code> rule flags log groups with infinite
(or excessive) retention and estimates the savings from capping them - alongside 88
other cost rules:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://releases.dragonfractal.com/install.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cca scan </span><span class="token parameter variable" style="color:#36acaa">--provider</span><span class="token plain"> aws</span><br></span></code></pre></div></div>
<p>The agent runs in your environment with read-only access, so your AWS credentials
never leave it. <a href="https://cca.dragonfractal.com/docs/providers/aws#required-iam-permissions" target="_blank" rel="noopener noreferrer" class="">See the AWS setup and required IAM permissions</a></p>
<section class="related-articles margin-vert--lg"><h2>Related Articles</h2><ul class="clean-list"><li><a href="https://cca.dragonfractal.com/blog/aws-cost-optimization-checklist">The AWS cost optimization checklist: 9 fixes that actually move the bill</a></li><li><a href="https://cca.dragonfractal.com/blog/migrate-to-graviton">Graviton migration: ~20% cheaper compute for a one-line instance-type change</a></li><li><a href="https://cca.dragonfractal.com/blog/clean-up-old-ebs-snapshots">Old EBS snapshots: the backup pile quietly inflating your bill</a></li></ul></section>
<section class="cli-reference margin-vert--lg"><h2>CLI Reference</h2><ul class="clean-list"><li><code>aws logs describe-log-groups</code></li><li><code>aws logs put-retention-policy</code></li></ul></section>]]></content:encoded>
            <category>aws</category>
            <category>cloudwatch</category>
            <category>cost-optimization</category>
            <category>finops</category>
        </item>
    </channel>
</rss>