cd /news/developer-tools/azure-sql-managed-instance-vs-azure-… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-72927] src=dev.to β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

Azure SQL Managed Instance vs Azure SQL Database: How I Actually Made the Call

A developer at a company migrating two enterprise applications from on-premises SQL Server to Azure chose Azure SQL Managed Instance over Azure SQL Database after evaluating migration paths, compatibility, and operational requirements. The decision was driven by SQL MI's support for native .bak restore, SQL Server Agent jobs, and near-100% surface-area compatibility, which simplified the lift-and-shift of a 200 GB production database.

read11 min views1 publishedJul 25, 2026

A field decision from a real production migration β€” not a feature-matrix blog.

When we migrated two internal enterprise applications (plus an integration layer) from on-premises SQL Server to Azure, the single most consequential decision in the whole programme was not the App Service tier, not the CDN, not the CI/CD design. It was the database platform. Azure gives you two serious PaaS options for SQL Server workloads β€” Azure SQL Database and Azure SQL Managed Instance (SQL MI) β€” and almost every comparison article online reads like a spec sheet. This post is different. It's the actual reasoning I went through, the evidence I gathered, the objections I had to answer during architecture review, and the trade-offs I knowingly accepted.

If you're staring at the same fork in the road, I hope walking through my decision saves you a few weeks.

Context matters more than features, so here's ours:

That last point is important. This wasn't a greenfield app designed cloud-native. This was a lift-and-shift of a living, heavily-scheduled, single-database system that three services depend on simultaneously.

Azure SQL Database is the "born in the cloud" option β€” a database-scoped service, fast to provision, cheap to start, with elegant HA options like zone-redundant compute and active geo-replication. I took it seriously. In fact, during the evaluation I fully sketched out what a production + DR architecture on Azure SQL Database would look like for us: a General Purpose single database at 16 vCores with zone-redundant compute in the primary region, an asynchronous geo-replica standing by in a secondary region, 7-day PITR for operational recovery, and long-term retention for compliance. On paper it was a beautiful design.

SQL Managed Instance is the "SQL Server, but managed" option β€” an instance-scoped service deployed inside your own virtual network, with near-100% surface-area compatibility with on-prem SQL Server.

The honest question I forced myself to answer: does our workload actually need the instance, or am I just choosing the familiar thing?

During architecture review, this exact challenge came back to me formally: "Re-validate whether SQL Managed Instance is the intended PROD choice." Someone has to defend the decision with evidence, and that someone was me. Here is the defence, factor by factor.

The migration path itself decided a lot.

Our database was ~200 GB of production data. The fastest, safest, most verifiable way to move a SQL Server database of that size is a native .bak backup restored directly into the target β€” full fidelity, all objects, all settings, one operation.

Only SQL MI supports native .bak restore (

RESTORE DATABASE ... FROM URL

, pointing at a backup uploaded to blob storage). Azure SQL Database does not accept .bak

files at all β€” your options there are BACPAC import (a logical export/import that is painfully slow and fragile at 200 GB scale, and drops things a logical export can't carry) or transactional replication / data migration tooling, which adds days of setup and validation for a one-time move.For us this alone nearly closed the argument. When your cutover window is a weekend and your database is 200 GB, "restore the .bak, run DBCC, point the connection strings" is a plan you can rehearse and trust. In our execution plan, the restore was literally a single day on the critical path: instance provisioned on day two, .bak

restored on day three, app-to-database connectivity validated on day five.

Lesson: the migration mechanism is a first-class selection criterion, not an afterthought. A platform you can't get your data into cleanly is not a candidate.

This is the one that people underestimate the most.

Our database had more than 25 SQL Server Agent jobs. Azure SQL Database has no SQL Server Agent. Every one of those jobs would need to be re-platformed β€” rewritten as Elastic Jobs, Azure Functions, Logic Apps, or pipeline schedules β€” then individually re-tested, re-monitored, and re-documented.

That's not a migration task; that's a re-engineering project hiding inside a migration. Twenty-five-plus jobs means twenty-five-plus opportunities for a schedule to silently not fire in production, for a permission model to differ, for an ops runbook to go stale. On our timeline, with our team size, that risk was simply not sellable.

SQL MI ships with SQL Agent intact. Our jobs restored with the database in the same .bak

, on the same schedules, with the same T-SQL. Zero rewrites. Zero behavioural drift.

Lesson: count your Agent jobs before you count vCores. If the number is more than a handful, SQL Database's per-job re-engineering cost usually dwarfs any infrastructure saving.

Azure SQL Database is priced and scoped per database (elastic pools soften this, but the model is still database-centric). SQL MI is priced per instance β€” and an instance hosts many databases behind one compute allocation, one security boundary, one endpoint.

Our situation: one database today, but a realistic pipeline of up to six more internal application databases that could consolidate onto the same instance later. On MI, each new database is a restore β€” no new resource, no new networking, no new monitoring plumbing, no new line on the invoice. Cross-database queries, shared logins, and instance-level configuration all behave exactly as the applications and DBAs expect from on-prem.

We provisioned the production instance as General Purpose, premium-series hardware, 6 vCores, 256 GB storage β€” deliberately sized so the instance is the platform, not just a container for one database.

Lesson: if your organisation thinks in terms of "a SQL Server that hosts our databases" rather than "a database per app," MI's instance model matches your operating reality and your chargeback model.

This one I didn't fully appreciate at selection time; it became decisive later, which retroactively validated the choice.

Remember: three services share one database. Two are user-facing applications; the third is an integration layer that receives bursty machine-to-machine traffic through an API gateway. Once everything was live and we started planning load tests, the obvious question surfaced: what stops an integration burst β€” say, 100,000 calls in a ramp β€” from starving the user-facing apps at the database layer?

On Azure SQL Database, the honest answer inside a single database is: not much. You can throttle upstream, tune queries, or physically split the workload into separate databases (which our schema and cross-service data model made unattractive).

On SQL MI, the answer is Resource Governor β€” fully supported on Managed Instance, unavailable on Azure SQL Database. We gave the integration service its own SQL login, then:

-- Hard-capped pool for the integration workload
CREATE RESOURCE POOL IntegrationPool WITH (
    MAX_CPU_PERCENT   = 30,
    CAP_CPU_PERCENT   = 30,   -- hard cap even when the instance is idle
    MAX_MEMORY_PERCENT = 30
);

CREATE WORKLOAD GROUP IntegrationGroup WITH (
    IMPORTANCE = LOW,
    REQUEST_MAX_MEMORY_GRANT_PERCENT = 25,
    MAX_DOP = 2
) USING IntegrationPool;

-- Classifier: route sessions by login
CREATE FUNCTION dbo.fn_WorkloadClassifier() RETURNS SYSNAME
WITH SCHEMABINDING AS
BEGIN
    RETURN (CASE WHEN SUSER_SNAME() = 'integration_service_user'
                 THEN 'IntegrationGroup' ELSE 'default' END);
END;

ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = dbo.fn_WorkloadClassifier);
ALTER RESOURCE GOVERNOR RECONFIGURE;

Now the integration layer can never take more than ~30% of instance CPU and memory, and under contention the user-facing queries win the scheduler. We designed this as one layer of a three-layer isolation strategy: rate-limiting policies at the API gateway (reject excess load cheaply at the edge), Resource Governor at the instance (contain whatever gets through), and β€” in the next phase β€” read offload to a readable geo-replica using ApplicationIntent=ReadOnly

. The Resource Governor caps are being validated as part of the load-test ramp itself.

Two honest caveats from the trenches: on the General Purpose tier, IO comes from remote storage and Resource Governor doesn't fully cap IO pressure β€” a badly written scan-heavy query can still hurt, so pair the caps with MAX_DOP

limits and index tuning. And keep the classifier function trivially simple, because it executes on every login.

Lesson: if multiple workloads share one database and you can't split them, Resource Governor is a platform-level answer that only MI gives you. That capability alone can justify the instance.

Our security bar was explicit: no public path to the database, ever.

SQL MI deploys inside your VNet, into a dedicated, delegated subnet, with a private IP address. In our build, an NSG restricts inbound database traffic to port 1433 from the app-integration subnet only; the app services reach it over VNet integration; the deployment pipeline reaches it through a self-hosted agent VM inside the same network. When the security review later asked us to "configure private endpoint / disable public access / restrict to VNet," the answer for the database line items was simply: it is already like that β€” it was built that way.

Azure SQL Database can absolutely be made private (private endpoints, deny public network access), but private is something you add to it. On MI, private is the starting state. For a compliance-sensitive workload, starting private removed a whole class of review findings before they existed.

One hard-won caution: verify, don't assume. We made it a standing gap-register item to confirm that the instance's optional public data endpoint stays disabled, and we learned separately that ARM template exports do not reliably surface security state (we caught a Transparent Data Encryption discrepancy only by querying sys.dm_database_encryption_keys

directly). Trust the engine's DMVs over exported templates.

The fear with MI is that you're choosing the "legacy-friendly" option and losing PaaS goodness. In practice we kept everything that mattered:

So the real comparison was never "PaaS vs not-PaaS." Both options are PaaS. The comparison was database-scoped PaaS vs instance-scoped PaaS, and our workload is instance-shaped.

No honest decision write-up skips this part. Choosing MI cost us three things, and each went into the risk register explicitly rather than being hand-waved:

1. Provisioning lead time. An MI takes hours to provision (ours was a 4–6 hour operation) versus minutes for Azure SQL Database. This made the instance the critical path of the entire migration plan β€” if the MI slipped, everything shifted right. We managed it by kicking off instance creation first thing on day two and running everything else in parallel. It also means MI-based DR-by-geo-restore has a long realistic RTO, because a replacement instance also takes hours to provision β€” which leads to…

2. DR posture is a phased decision, not a default. Our initial deployment is single-zone, no failover group. We documented this as an explicit, accepted risk with a named decision owner: regional DR today means geo-restoring into a freshly provisioned instance (RTO measured in hours). The approved next-phase path is a compute upgrade to 16 vCores with zone-redundant compute plus a geo-replica in a secondary region β€” which also becomes the read-offload target in the isolation strategy above. On Azure SQL Database that HA/DR elegance is cheaper and faster to switch on; on MI it's a bigger, more deliberate investment. We chose to phase it rather than pay for it on day one.

3. General Purpose IO characteristics. GP-tier MI uses remote storage; our instance runs at a defined IOPS envelope, and IOPS scale with file size. Resource Governor caps CPU and memory but not IO pressure from unoptimized queries. Mitigation: query tuning on the hot paths, DOP limits, an IO alert with dynamic thresholds, and a storage-capacity alert at 80% β€” plus load testing before we declare victory.

If I strip everything down to the scorecard I effectively used:

Criterion Azure SQL Database SQL Managed Instance Weight for us
Native .bak restore of a ~200 GB DB
βœ— (BACPAC/DMS only) βœ“ single restore operation Deal-breaker
25+ SQL Agent jobs βœ— re-engineer every job βœ“ restore as-is Deal-breaker
Many DBs, one platform, one price Per-database model Instance hosts them all High
Workload isolation on a shared DB βœ— no Resource Governor βœ“ Resource Governor High (proven later)
Private-by-default networking Add-on (private endpoint) Native (VNet, private IP) High
Managed backups / PITR / LTR / HA βœ“ βœ“ Parity
Provisioning speed Minutes Hours Accepted cost
Zone-redundant HA / geo-replica economics Cheaper, simpler Bigger, phased investment Accepted cost

Two deal-breakers, three strong instance-shaped advantages, feature parity on the PaaS fundamentals, and two costs we consciously accepted and mitigated. That's not a preference β€” that's a verdict.

Forget the feature matrix for a moment and ask four questions about your workload:

.bak

restore is the only migration path you can rehearse and trust at your size, MI is already ahead.If you answered "greenfield app, one database, no jobs, no shared workloads" β€” take Azure SQL Database and enjoy the faster provisioning and cheaper HA. That's the right tool for that shape.

Ours wasn't that shape. We chose the instance, defended it in review with evidence, and it has since paid us back in exactly the ways the analysis predicted β€” most satisfyingly the day we needed to fence off a bursty integration workload and the platform already had the tool waiting.

Choose the platform that matches the shape of your workload β€” not the one that matches the shape of the marketing page.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @azure sql managed instance 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/azure-sql-managed-in…] indexed:0 read:11min 2026-07-25 Β· β€”