
An opinionated guide based on real-world production experience
The $$$$$/Year Mistake Most Developers Make
Here’s a scenario that plays out constantly: You’re building a mobile app or web API (or XAF Blazor). You choose Azure App Service (great choice) and Azure SQL Database (also great). You read about “serverless” databases that “auto-scale” and “only charge for what you use.” Sounds perfect, right?
Wrong.
I’ve seen this exact pattern cost developers 2-3x what they should be paying, all while dealing with mysterious timeout errors and frustrated users. Let me show you how to avoid this trap.
The Serverless Seduction (And Why It Fails)
Azure SQL Serverless sounds amazing in the marketing materials:
- Auto-scales based on demand
- Auto-pauses when idle
- Pay only for compute time used
- Perfect for “unpredictable workloads”
Here’s what they don’t tell you: For production mobile apps and web APIs, serverless is almost always the wrong choice.
The Cold Start Death Spiral
Serverless databases auto-pause after 1 hour of inactivity. Great for saving money, terrible for user experience. When a paused database receives a request, it takes 10-30 seconds to wake up.
For a user trying to log into your mobile app at 8am, that’s an eternity. The request times out. They try again. Now it works because the database is awake.
But the damage is done – you look unprofessional, and your 1-star review is already submitted.
The Health Check Band-Aid
So developers do what seems logical: add a health check that pings the database every few minutes to keep it awake.
Congratulations, you just destroyed serverless’s value proposition.
Your database never pauses. You’re now paying serverless rates 24/7, which is typically 2-3x more expensive than provisioned tiers offering the same performance.
Real example from production:
Serverless Gen5 2 vCore (kept awake 24/7): $366/month Standard S2 Provisioned (50 DTU): $73/month Same database, actual usage: 1% CPU Cost per useful DTU: Serverless pays $366 for 1 DTU of work
That’s a $3,516/year mistake for literally worse performance (occasional cold starts slip through).
The Right Setup: Match Your Database to Your Traffic Pattern
Here’s my opinionated framework for choosing the right Azure SQL tier:
Production Mobile/Web Apps → Provisioned Tier (Always)
When:
- Customer-facing applications
- Users expect instant response
- Running on Standard tier or higher App Service
- Any 24/7 availability requirement
Use:
- Standard S1 ($30/month): Light traffic apps, < 100 daily active users
- Standard S2 ($73/month): Most mobile apps, < 500 daily active users
- Standard S3 ($147/month): Heavy traffic apps, < 2000 daily active users
Why provisioned wins:
- Zero cold starts (always instant response)
- Predictable performance
- Simpler architecture (no retry logic needed)
- Often cheaper than serverless with health checks
- Professional user experience
Dev/Test/Staging → Serverless (Perfect Use Case)
When:
- Development databases used only during work hours
- Staging environments used only during deployments
- Internal tools with sporadic usage
- Proof-of-concept projects
Use:
- Serverless: 0.5-1 vCore min, 2-4 vCore max
- Auto-pause: 60 minutes
- No health checks (let it pause!)
Why serverless wins here:
- Actually saves money (database idles nights/weekends)
- Cold starts acceptable (developers can wait 30 seconds)
- Perfect for unpredictable dev/test workloads
- Can save 60-80% vs provisioned
The Decision Tree
Is this production with real users?
├─ Yes → Use Provisioned (Standard tier)
│ └─ Match DTU to actual usage (measure first!)
│
└─ No → Can users tolerate 30-second delays?
├─ Yes → Use Serverless (let it pause)
└─ No → Use Provisioned anyway
The App Service Side: Don’t Cheap Out
I see developers trying to save $50/month on App Service while running Premium databases. This is backwards thinking.
The Tier That Makes Sense: Standard S1 or Premium V3 P1V3
Standard S1 (~$70/month):
- 1 vCPU, 1.75GB RAM
- Always On feature (critical!)
- Deployment slots
- Auto-scaling capability
- Perfect for most APIs
Premium V3 P1V3 (~$93/month):
- 2 vCPU, 8GB RAM
- Better price/performance than older Premium tiers
- VNet integration if needed
- Faster performance
- Worth it for production apps
Never Use These for Production
Free/Shared Tiers:
- Unloads after 20 minutes (same cold start problem as serverless SQL)
- Shared resources with other tenants
- No SLA
- Looks unprofessional when your API times out
Basic Tier:
- No “Always On” feature
- Can still unload during low traffic
- No deployment slots (risky deployments)
- Save $20/month, lose reliability
The Always On Requirement
The “Always On” feature (available in Standard tier and above) is non-negotiable for production. It keeps your app loaded in memory even with no traffic.
Without Always On:
No traffic for 20 minutes → App unloads User hits API → 5-10 second cold start Poor user experience
With Always On:
No traffic for hours → App stays loaded User hits API → Instant response (< 100ms) Professional experience
Real-World Cost Optimization: A Case Study
Let me show you a real production setup and how to optimize it:
The Starting Point (Wasteful)
Configuration:
- Azure App Service: Premium V3 P1V3 (~$93/month)
- Azure SQL: Serverless Gen5 2 vCore (~$366/month with 24/7 health checks)
- Health check: Pinging database every 3 minutes to prevent cold starts
- Actual database usage: 1% CPU (massively over-provisioned)
Total cost: $459/month
Problems:
- Paying serverless rates for provisioned behavior
- Over-provisioned compute (using 1% of capacity)
- Unnecessary complexity (health check workaround)
The Optimized Setup
Configuration:
- Azure App Service: Premium V3 P1V3 (~$93/month) – Keep as-is
- Azure SQL: Standard S2 (50 DTU) (~$73/month)
- Health check: Keep in place (no harm on provisioned)
- Capacity: 50x actual usage (massive headroom for growth)
Total cost: $166/month
Improvements:
- Zero cold starts (better UX)
- Simpler architecture (no workarounds needed)
- Room to grow 10x before hitting limits
- Saves $3,516/year
Why This Works
The database was using 1 DTU at peak load. Standard S2 provides 50 DTU capacity. That’s 50x headroom with the health checks consuming negligible resources.
Key insight: Measure your actual usage before choosing a tier. Most developers over-provision by 10-50x.
The Measurement-First Approach
Before choosing any database tier, run this query for 7 days:
SELECT
AVG(avg_cpu_percent) as avg_cpu,
MAX(avg_cpu_percent) as max_cpu
FROM sys.dm_db_resource_stats
WHERE end_time > DATEADD(day, -7, GETUTCDATE());
Interpretation:
- avg_cpu < 20%: You’re massively over-provisioned, scale down
- avg_cpu 20-50%: Good utilization with headroom
- avg_cpu 50-70%: Right-sized, monitor for growth
- avg_cpu > 70%: Scale up soon to avoid performance issues
- max_cpu > 90%: Scale up immediately
DTU to vCore Translation
Rough equivalence for planning:
1 vCore (Serverless/Provisioned) ≈ 50 DTU (Standard tier) 2 vCore ≈ 100 DTU (Standard S3) 4 vCore ≈ 200 DTU (Standard S4)
Use this to convert between tiers when optimizing.
The Elastic Pool Opportunity
If you have multiple databases (prod + staging + dev), elastic pools can save significant money.
Individual Databases:
Production: Standard S2 ($73/month) Staging: Standard S1 ($30/month) Development: Standard S1 ($30/month) Total: $133/month
Elastic Pool:
Standard 100 eDTU Pool: $147/month All three databases share resources Total: $147/month
When pools make sense:
- 2+ databases with different usage patterns
- Not all databases busy simultaneously
- Development/staging used sporadically
When to skip pools:
- Single production database only
- All databases need guaranteed dedicated resources
- Databases consistently busy at same time
Common Mistakes to Avoid
1. The “Serverless is Modern” Fallacy
Serverless isn’t better because it’s newer. It’s a cost optimization for specific workload patterns (truly idle databases). For 24/7 production apps, provisioned is the right choice.
2. Under-Provisioning App Service
Don’t run Standard S1 databases on Free tier App Service. Your database won’t be the bottleneck – your app will crash under load. Match your tiers appropriately.
3. Ignoring Backup Costs
Geo-redundant backup costs ~50% more than locally-redundant. Unless you have compliance requirements or need disaster recovery across regions, stick with locally-redundant backup.
Most mobile apps don’t need geo-redundant backup.
4. The “I’ll Optimize Later” Trap
Starting with over-provisioned resources “to be safe” often means never optimizing. You get used to the cost, and inertia sets in.
Start right-sized based on actual measurements or conservative estimates.
5. Not Using Connection Pooling
Configure your connection string properly:
Server=tcp:yourserver.database.windows.net,1433; Database=yourdb; User ID=user; Password=pass; Encrypt=True; Connection Timeout=30; Min Pool Size=10; Max Pool Size=100; Pooling=true;
This alone can reduce database load by 50-80% for high-traffic APIs.
The Opinionated Recommendations
Here’s what I recommend for different scenarios:
Startup MVP / Side Project
App Service: Standard S1 ($70/month) Database: Standard S1 ($30/month) Total: $100/month Rationale: Professional performance, room to grow, affordable
Production Mobile App (< 500 DAU)
App Service: Premium V3 P1V3 ($93/month) Database: Standard S2 ($73/month) Total: $166/month Rationale: Great performance, reliable, scales to 5000+ DAU
Production Mobile App (500-2000 DAU)
App Service: Premium V3 P1V3 ($93/month) Database: Standard S3 ($147/month) Total: $240/month Rationale: Handles growth, consistent performance
Enterprise Application
App Service: Premium V3 P2V3 or P3V3 ($186-372/month) Database: Premium P1 or Business Critical ($465+/month) Consider: Elastic pools for multiple databases Rationale: SLA requirements, advanced features, high availability
Development Environment
App Service: Standard S1 ($70/month) or share with prod Database: Serverless 0.5-2 vCore ($30-80/month) Let it auto-pause (no health checks!) Rationale: Saves money when idle, acceptable cold starts
The Bottom Line
Stop using Azure SQL Serverless for production applications. It’s marketed as “pay for what you use” but ends up costing more while providing worse performance for 24/7 workloads.
The winning formula:
- Measure actual usage before choosing a tier
- Use Provisioned (Standard tier) for production apps
- Use Serverless only for dev/test/sporadic workloads
- Enable “Always On” for App Service (Standard tier minimum)
- Right-size based on data, not fear of running out of capacity
Real cost optimization comes from:
- Matching tier to actual usage (not maximum possible usage)
- Choosing the right model (provisioned vs serverless) for your pattern
- Measuring and adjusting based on real metrics
- Not over-engineering for scale you don’t have yet
A well-architected Standard S2 database ($73/month) will outperform a poorly-configured Serverless setup ($366/month) every single time.
Measure. Right-size. Profit.
Quick Reference: Decision Matrix
| Your Situation | App Service | SQL Database | Monthly Cost |
|---|---|---|---|
| MVP / POC | Standard S1 | Standard S1 | ~$100 |
| Production Mobile App | Premium V3 P1V3 | Standard S2-S3 | ~$166-240 |
| High Traffic App | Premium V3 P2V3+ | Standard S4+ or Premium | $400+ |
| Dev/Test Environment | Standard S1 (or shared) | Serverless 0.5-2 vCore | ~$100-150 |
| Internal Tool (sporadic) | Standard S1 | Serverless 0.5-1 vCore | ~$100-120 |
Remember: These are starting points. Measure your actual usage and adjust accordingly. The goal is predictable performance at the lowest cost that meets your SLA requirements.
The one rule to remember: If you’re adding health checks to keep a serverless database awake, you’ve chosen the wrong tier. Switch to provisioned and save money while improving reliability.