6 Azure Cost Mistakes That Will Shock You When the Bill Arrives
Synapse pools billing overnight. Databricks clusters, nobody terminated. ADF retries running silently till morning. These are the charges…
6 Azure Cost Mistakes That Will Shock You When the Bill Arrives
Synapse pools billing overnight. Databricks clusters, nobody terminated. ADF retries running silently till morning. These are the charges that show up after it’s too late to do anything about them.
**Non-member, click here to read**
Photo by Pawel Czerwinski on Unsplash
Nobody looks at an Azure bill expecting a bad surprise.
And yet here we are. Experienced engineers, well-run teams, pipelines that work fine, and a number at the end of the month that makes no sense. Not because anyone was reckless.
Because Azure doesn’t stop the meter when you forget something. There are no hard limits. No automatic shutdowns. Just alerts, and only if you set them up right, which most teams don’t.
Services keep running. Charges keep building. The bill lands and suddenly there’s a meeting nobody wanted to have.
Every mistake on this list has shown up on a real Azure bill. A few of them I’ve seen more than once. None of them are complicated. All of them are fixable in under an hour once you know what to look for.
1. Your Synapse Dedicated SQL Pool Is Running While Everyone Sleeps
A dedicated SQL pool doesn’t care that your analysts went home at 6pm. It doesn’t care that it’s Saturday. It runs, and Azure charges you the same rate whether 200 people are querying it or nobody is.
Most teams know they should pause it. Most teams don’t, because resuming takes a few minutes, and at some point, someone decided that wait time wasn’t worth the hassle.
That one decision quietly costs hundreds every month. A DW400c pool running around the clock, when the business only needs it for 10 hours on weekdays, means you’re paying for roughly 130 hours of compute that sat completely idle.
The fix is about 20 minutes of setup. Once.
# Use an ADF Web Activity at the end of your nightly pipeline to pause the pool
# POST https://management.azure.com/subscriptions/{sub}/resourceGroups/{rg}
# /providers/Microsoft.Synapse/workspaces/{workspace}/sqlPools/{pool}/pause
# Schedule resume before business hours start
# If the pipeline doesn't run, the pool stays paused - which is fine
Pause at end of business. Resume before start. Bolt it onto your existing pipeline as the last activity. You pay for what runs, not for what sits there billing you through the night.
2. Egress Charges Nobody Thought to Budget For
Ask a data engineer what Azure costs money, and you’ll hear about DIUs, DBUs, and SQL pool compute. Egress rarely comes up.
Until it shows up as a line item, nobody recognizes it.
Moving data out of Azure costs money. Moving data between Azure regions costs money. A pipeline replicating from the East US to West Europe every hour, or exporting data to an on-premises system nightly, or feeding a third-party analytics tool outside Azure—each of these generates egress charges.
None of them feels expensive when you design them. Together, they can add a few hundred to a bill that was already finalized in someone’s head.
# Pull egress charges from Azure Cost Analysis
az consumption usage list \
--billing-period-name 202506 \
--query "[?contains(meterCategory,'Bandwidth')]" \
--output table
# Or in the portal: Cost Analysis > filter Meter Category = "Bandwidth"
# Group by Resource to see which pipeline or service is generating it
It rarely changes the design. It just needs to be in the budget before the pipeline goes live, not discovered afterward.
3. Databricks Clusters Still Running Six Hours After the Job Finished
Interactive clusters don’t shut down on their own. Not unless someone configured them to.
The pattern is always the same. Someone spins up a cluster, runs a notebook, and gets pulled into something else. The cluster sits there running for six hours, doing absolutely nothing.
It shows up as a normal line item in Databricks billing because technically, the cluster was healthy. No error, no alert. Just a meter running on a resource nobody was using.
This happens more often than people admit because there’s no visible sign of it unless you go looking.
{
"autotermination_minutes": 30,
"cluster_name": "dev-interactive",
"spark_version": "13.3.x-scala2.12",
"node_type_id": "Standard_DS3_v2"
}
Set auto-termination to 30 minutes on every interactive cluster. Not 120, not 60. Thirty. If someone is actively using it, they’ll restart it. If nobody is using it, it should be off.
Job clusters are different—they terminate when the job finishes. If your scheduled production jobs are running on interactive clusters, that’s worth fixing today.
4. Budget Alerts That Only Fire After the Damage Is Done
Most teams have a budget alert. It’s set at the subscription level, maybe 20% above expected monthly spend, and it fires as an email to someone in finance.
By the time the alert fires, the money is already spent. And by the time finance forwards the email, another day has passed.
What actually helps is anomaly detection at the service level. When Databricks spend doubles compared to yesterday, you want to know that day—not when the monthly total crosses a threshold three weeks later.
Azure Cost Management has built-in anomaly detection. It’s free. Most teams don’t use it.
// Set anomaly alerts per service, not just total subscription
{
"alertType": "Anomaly",
"scope": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Databricks/workspaces/{ws}",
"notificationEmails": ["data-engineering-team@company.com"],
"threshold": {
"operator": "GreaterThan",
"value": 20
}
}
Two things worth setting up: a budget alert at 80% of expected monthly spend per service (Databricks, Synapse, and ADF separately), and anomaly detection so a spike shows up when it happens, not when the bill arrives.
And route alerts to an engineering Slack channel or Teams. Not just to a finance inbox. By the time finance loops you in, two or three more days have run.
5. ADF Retry Storms Nobody Was Watching
This one surprises people who’ve been using ADF for years.
ADF charges per activity run. Not per pipeline. Per activity. A pipeline with 20 activities and 3 retries configured on each generates 80 activity runs in a normal, successful execution.
When something breaks, and those retries fire every hour through the night — 20 activities times 3 retries times 8 hours — you’re looking at thousands of activity runs by the time someone checks in the morning.
The free tier covers 1,000 activity runs per month. A single retry storm on a complex pipeline can exceed that in one night.
// this looks harmless
{
"name": "CopyActivity",
"policy": {
"retry": 3,
"retryIntervalInSeconds": 30
}
}
// 20 activities x 3 retries x hourly schedule x 8 hours overnight = 480 extra runs
// on top of your normal usage, from one broken pipeline
// for non-critical pipelines, this is safer
{
"policy": {
"retry": 1,
"retryIntervalInSeconds": 300
}
}
Pull the activity run count from ADF monitoring for the last month. If the number is significantly higher than the pipeline run times activity count, something was retrying more than expected. Add a pipeline failure alert that fires immediately so broken pipelines don’t run unattended all night.
6. Dev Environments Billed at Production Rates
Most Azure dev environments are production environments with a different name.
Someone needed a dev setup fast and copied the Terraform from production, and the ticket was closed. Same Synapse dedicated pool tier. Same Databricks cluster size. Same storage redundancy. The data is smaller, but the infrastructure cost is identical.
A DW400c pool in dev costs the same per hour as a DW400c pool in production. That’s not a dev cost; it’s a production cost labeled dev. And because dev environments tend to run continuously without the pause schedules that production sometimes has, they can end up costing more.
A dedicated pool spun up with default settings for what someone expected to be a quick training exercise can hit $500 in two days. Default settings are production settings.
// dev Databricks cluster — this is what dev should look like
{
"num_workers": 0,
"azure_attributes": {
"availability": "SPOT_WITH_FALLBACK_AZURE",
"spot_bid_max_price": -1
},
"autotermination_minutes": 20
}
// dev Synapse - use serverless, not dedicated
// dev storage - LRS, not GRS or ZRS
// ADF - same tier is fine, ADF itself is not expensive
The rule for non-production environments doesn’t need to be complicated. Serverless where possible. Spot instances for compute. Auto-shutdown on everything. Enforce it before resources get created, not after the bill shows up.
Check These 6 Things Before the Next Bill Arrives
Synapse dedicated pools—is there a pause schedule? If not, that’s today’s task.
Databricks interactive clusters—does every one have auto-termination set? Check all of them.
Cost Analysis—filter by Bandwidth meter category. Anything unexpected?
ADF monitoring — pull last month’s activity run count. Does it match your pipeline schedule math?
Budget alerts — anomaly detection per service, not just a total subscription alert?
Dev environment resources — how many are running production-tier config?
None of this takes long. Most of it takes under an hour total. The bill takes thirty days to arrive and ten minutes to become a problem.
Azure charges accurately. It doesn’t warn you, it doesn’t stop, and it has no sympathy for a config that made sense at the time. The engineers who avoid surprises aren’t the ones spending less — they’re the ones who look at the right numbers before finance has to.
I write about Azure data engineering and what it actually costs to run things properly. Follow if that’s useful.
메타데이터
- post_id
- ab2db7b7922c
- slug
- 6-azure-cost-mistakes-that-quietly-drain-your-budget-and-how-to-fix-each-one-ab2db7b7922c
- url
- https://medium.com/towards-data-engineering/6-azure-cost-mistakes-that-quietly-drain-your-budget-and-how-to-fix-each-one-ab2db7b7922c
- canonical_url
- https://medium.com/towards-data-engineering/6-azure-cost-mistakes-that-quietly-drain-your-budget-and-how-to-fix-each-one-ab2db7b7922c
- author_url
- https://medium.com/@sauravsinghsisodiya
- status
- ok
- fetched_at
- 2026-06-15 20:49:13