Situation

I Was Cleaning Up My Personal AWS Account Recently And Glue Kept Showing Up Near The Top Of The Bill, Around $10 A Month. As Far As I Know I’m Not Running Any Glue Jobs Right Now, So I Went After The Usual Suspects First: Jobs, Dev Endpoints, Interactive Sessions, Triggers, Workflows. All Of Them Were Already At Zero. The Next Month’s Bill Didn’t Move. Grouping The Cost By Usage Type Finally Showed Where It Came From: Every Cent Was USE1-Crawler-DPU-Hour, Three Crawlers In us-east-1 With No Schedule On Them, Created Back In 2024-04 By The Official CUR CloudFormation Template.

Result First:

Where The Money Went USE1-Crawler-DPU-Hour, 22.35 DPU-Hours In 2026-09, About $9.84. Catalog Storage And Requests Stayed Inside The Free Tier
What I Deleted First Jobs, Dev Endpoints, Sessions, Triggers, Workflows. Already Zero, Saved Nothing
What Was Actually Running Three CUR Crawlers, No Schedule, Started By A Lambda Every Time A CUR Report Landed In S3
Why It Never Stopped Three HOURLY Reports, Each Delivered About 3 Times A Day, Each Delivery Starts A CRAWL_EVERYTHING Crawl That Rescans The Whole Report Prefix. The Line Went From $3.45 In 2025-10 To About $10 Now: About A Third From More Runs, Two Thirds From Each Run Billing About Twice The DPUs. Runs Actually Got Shorter
Fix Deleted The Three CUR Report Definitions (Backed Up As JSON First). Kept The Crawlers, Stacks And Glue Database
Verified Checked On 10-03. No Crawler Has Run Since The Last Delivery Before The Deletion, Around 08:19 On 10-02

Turns Out “No Schedule” Doesn’t Mean “Never Runs”. The Thing Starting These Crawlers Wasn’t In Glue At All.

How I Found It

Cost Explorer Grouped By Service Only Told Me “Glue”. Switching The Group By To Usage Type Was The Step That Mattered:

Usage Type 2026-09
USE1-Crawler-DPU-Hour 22.35 DPU-Hours, About $9.84
Catalog Storage / Requests Free Tier

That One Row Ruled Out Everything I Had Already Deleted. Jobs And Sessions Bill Under Different Usage Types. This Was Crawlers, And Only In us-east-1.

How It Broke (Root Cause)

The Crawlers Had No Schedule

Listing The Crawlers Was The First Surprise. Schedule Was Empty On All Of Them, Yet Three Had Recent Runs:

aws glue get-crawlers --region us-east-1 \
  --query 'Crawlers[].{Name:Name,Schedule:Schedule.ScheduleExpression,Recrawl:RecrawlPolicy.RecrawlBehavior,LastStart:LastCrawl.StartTime}' \
  --output table

The Account Has More Crawlers Than That From Old Experiments, But Only The Three Named AWSCURCrawler-* Were Actually Running. All Of Them Use CRAWL_EVERYTHING.

The Trigger Lives In The Template, Not In Glue

Those Names Come From The CloudFormation Template CUR Gives You When You Enable Athena Integration For A Report. I Had Deployed It A Few Times In 2024-04 While Testing CUR, And Forgot About It. The Chain It Sets Up Looks Like This:

CUR writes a report file into S3
  -> S3 event notification
    -> AWSCURInitializer Lambda
      -> glue:StartCrawler
        -> CRAWL_EVERYTHING over the whole report prefix

You Can See The Pieces Without Opening Any Console:

# Which CUR reports exist, and how often they're delivered
aws cur describe-report-definitions --region us-east-1 \
  --query 'ReportDefinitions[].{Name:ReportName,TimeUnit:TimeUnit,Bucket:S3Bucket,Prefix:S3Prefix}' \
  --output table

# The Lambdas the template created to start the crawlers
aws lambda list-functions --region us-east-1 \
  --query 'Functions[?contains(FunctionName, `AWSCURInitializer`)].FunctionName' \
  --output text

So The Schedule Was Never In Glue. It Was CUR’s Delivery Cadence. Three Reports, All HOURLY, Each One Rewritten Into S3 About Three Times A Day, Each Write Starting A Full Crawl.

$3.45 Then, About $10 Now

I Didn’t Change The Setup In That Year, Yet The Line Went From $3.45 In 2025-10 To About $10 In 2026-09. My First Guess Was “The Data Keeps Growing, So Each Run Takes Longer”. Then I Pulled Every Run Glue Still Had On Record (It Went Back About A Year, To 2025-10-05) And That Guess Was Wrong:

Per Month, All Three Crawlers 2025-10 2026-09
Runs About 179 264
Average Run Time 81 Seconds 50 Seconds
Average DPU-Hours Per Run 0.044 0.084
Total DPU-Hours About 7.85 22.21

(2025-10 Is Scaled Up From The 27 Days Still On Record. Both Totals Land Within 1% Of What I Was Billed.)

Runs Got Shorter, Not Longer. The Money Came From Two Places. Each Crawler Went From About 2 Runs A Day To About 3, Which Explains Roughly A Third Of The Increase. The Other Two Thirds Came From Each Run Burning About Twice The DPUs. The Runs Split Into Two Groups: Light Ones Around 150 DPU-Seconds, And Heavy Ones Around 420 DPU-Seconds That Actually Finished Faster. A Year Ago 3% Of Runs Were Heavy. In 2026-09 It Was 58%. The API Doesn’t Say Why A Run Lands In One Group Or The Other, And The Shift Doesn’t Follow A Steady “More Data” Curve, So I’m Not Going To Guess.

I Also Checked The Pricing Page, Which Says Crawlers Bill Per Second “With A 10-Minute Minimum Duration For Each Crawl”. I Couldn’t Find That Minimum In My Numbers. At 264 Runs, A 10-Minute Floor Would Mean At Least 44 DPU-Hours, Twice What I Paid, And The Bill Matches The Per-Run DPUHour Values Added Up. So Whatever That Minimum Does, It Wasn’t What Grew.

The Template Isn’t Doing Anything Wrong. Its Default Is To Rescan Everything On Every Delivery, And I Never Looked Again At A Stack I Deployed For A Test In 2024.

Notes

  • Deleting The Resources I Could See Didn’t Stop The Thing Calling Them. I Cleaned Up Glue From The Glue Side. The Cost Was Being Driven From CUR, Through S3 And Lambda, None Of Which Looks Like Glue.
  • An Empty Schedule Field Says Nothing About How Often Something Runs. Event Driven Starts Never Show Up There. LastCrawl.StartTime Is The Field That Tells The Truth.
  • Group By Usage Type Before Deleting Anything. I Would Have Skipped The Whole First Round If I’d Looked At The Usage Type Breakdown First.
  • Run Time Is Not Cost. My Runs Got About 40% Shorter While Cost Per Run Nearly Doubled. Split A Growing Bill Into Runs Times Cost Per Run, And Read Cost Per Run From The Billing Unit (DPUHour), Not From The Clock.
  • Pull The History While It’s Still There. list-crawls Only Went Back About A Year For Me. A Few More Months And The 2025-10 Baseline Would Have Been Gone.

The Fix

I Wanted To Keep The Crawlers, The Stacks And The Glue Database, Because I Might Query CUR Through Athena Again Later. So Instead Of Tearing Down The Whole Chain, I Cut It At The Source: The Report Definitions. No Report Delivery, No S3 Event, No Lambda, No Crawl.

Back Up The Definitions First, So Any Of Them Can Be Recreated Exactly:

aws cur describe-report-definitions --region us-east-1 --output json \
  > cur-report-definitions-backup.json

Then Delete Each Report Definition. This Is The One Step That Changes Anything:

aws cur delete-report-definition --region us-east-1 --report-name <report-name>

If I Need One Back, The Saved JSON Has Everything aws cur put-report-definition Needs.

How I Verified It

The Next Day (10-03) I Ran The Crawler Listing Again. None Of The Three Had Started Since The Last Delivery Before The Deletion, Around 08:19 On 10-02. No New Runs Means No New USE1-Crawler-DPU-Hour.

aws glue get-crawler-metrics --region us-east-1 \
  --query 'CrawlerMetricsList[].{Name:CrawlerName,LastRunSec:LastRuntimeSeconds,MedianRunSec:MedianRuntimeSeconds}' \
  --output table

One Honest Detail. This $10 Was Covered By Credits Every Month, So I Was Paying About $0 In Cash. But It Was The Biggest Thing Eating Those Credits In This Account, Well Ahead Of Second Place (ECR At $1.47). Credits Run Out. A Line Item That Grows On Its Own Is Worth Stopping Before They Do.

Read Only Checks You Can Paste

Everything Below Only Reads. Confirm Which Account You’re In First, CUR And These Crawlers Live In us-east-1:

aws sts get-caller-identity --query Account --output text

# 1. Crawlers: schedule, recrawl behavior, last start
aws glue get-crawlers --region us-east-1 \
  --query 'Crawlers[].{Name:Name,Schedule:Schedule.ScheduleExpression,Recrawl:RecrawlPolicy.RecrawlBehavior,LastStart:LastCrawl.StartTime}' \
  --output table

# 2. How long each crawl takes
aws glue get-crawler-metrics --region us-east-1 \
  --query 'CrawlerMetricsList[].{Name:CrawlerName,LastRunSec:LastRuntimeSeconds,MedianRunSec:MedianRuntimeSeconds}' \
  --output table

# 3. CUR reports still being delivered
aws cur describe-report-definitions --region us-east-1 \
  --query 'ReportDefinitions[].{Name:ReportName,TimeUnit:TimeUnit,Bucket:S3Bucket,Prefix:S3Prefix}' \
  --output table

# 4. Lambdas left behind by the CUR template
aws lambda list-functions --region us-east-1 \
  --query 'Functions[?contains(FunctionName, `AWSCURInitializer`)].FunctionName' \
  --output text

# 5. Every run Glue still remembers, with what each one billed
aws glue list-crawls --region us-east-1 --crawler-name <crawler-name> \
  --query 'Crawls[].{Start:StartTime,End:EndTime,State:State,DPUHour:DPUHour}' \
  --output table

If (1) Shows AWSCURCrawler-* With An Empty Schedule But A Recent LastStart, And (3) Still Lists The Matching Report, You Have The Same Chain I Had.

How To Prevent It

Scenario What To Do
A Service Costs Money But You “Don’t Use It” Group Cost Explorer By Usage Type Before Deleting Anything. It Tells You Which Kind Of Resource Is Billing
A Glue Crawler Has No Schedule But Keeps Running Look For Event Driven Starts: S3 Notifications, Lambdas Calling StartCrawler, Templates That Wired Them Up
You Deployed The CUR Athena Template To Try It Out Either Delete The Stack When You’re Done, Or Delete The Report Definition So Nothing Feeds It
You Want To Keep The Pipeline But Stop The Cost Cut It At The Source. Back Up The Report Definition As JSON, Delete It, Keep The Rest
A Cost Line Grows Slowly With No Config Change Look For Something That Runs On Its Own, Then Split The Bill Into How Often It Runs And What Each Run Bills. Read The Second One From The Billing Unit, Not The Run Time

Reference: