Azure SQL vs Cosmos DB vs PostgreSQL — Choose the Right Database
You have just built a brilliant application. Now comes the question that has sparked a thousand architecture review meetings: which database? Azure offers over a dozen managed database services, and picking the wrong one means either rewriting your data layer six months later or paying triple what you should. Let's cut through the confusion and build a real decision framework.
The Decision Tree
Before diving into individual services, start with three questions:
- Is your data relational? If you need joins, transactions, and foreign keys, go relational.
- Do you need global distribution? If your users are on every continent and need single-digit millisecond reads, Cosmos DB is built for this.
- What is your query pattern? Known schemas with complex queries favor SQL. Flexible, document-oriented access patterns favor NoSQL.
If you answered "relational" and your team already knows PostgreSQL, stop reading and go with Azure Database for PostgreSQL. If you need SQL Server compatibility, go Azure SQL Database. If you need multi-model, globally distributed, or massive write throughput, Cosmos DB is your answer.
Azure SQL Database
Azure SQL Database is a fully managed relational database built on the SQL Server engine. You get automatic patching, backups, and high availability without managing a VM.
DTU vs vCore Pricing
This is where people get confused. Azure SQL Database offers two purchasing models:
| Model | What It Is | Best For | Scaling |
|---|---|---|---|
| DTU | Bundled CPU + Memory + IO units | Predictable workloads, simpler billing | Basic (5 DTU) to Premium (4000 DTU) |
| vCore | Choose CPU cores and memory independently | Flexible sizing, reserved capacity discounts | 2 to 128 vCores |
DTU is simpler — you pick a tier and get a pre-configured bundle. vCore gives you control over individual resources and lets you bring your own SQL Server license (Azure Hybrid Benefit).
Creating an Azure SQL Database
# Create a resource group
az group create \
--name rg-database-demo \
--location eastus
# Create a SQL Server (logical server)
az sql server create \
--resource-group rg-database-demo \
--name sql-server-prod-2025 \
--admin-user sqladmin \
--admin-password 'YourStr0ngP@ssw0rd!' \
--location eastus
# Create a database with vCore model (General Purpose, 2 vCores)
az sql db create \
--resource-group rg-database-demo \
--server sql-server-prod-2025 \
--name db-inventory \
--edition GeneralPurpose \
--family Gen5 \
--capacity 2 \
--max-size 32GB
Elastic Pools
If you run multiple databases with unpredictable usage spikes, elastic pools let them share resources:
# Create an elastic pool
az sql elastic-pool create \
--resource-group rg-database-demo \
--server sql-server-prod-2025 \
--name pool-shared-prod \
--edition GeneralPurpose \
--family Gen5 \
--capacity 4 \
--max-size 128GB
# Move a database into the pool
az sql db update \
--resource-group rg-database-demo \
--server sql-server-prod-2025 \
--name db-inventory \
--elastic-pool pool-shared-prod
Ten databases each sized for peak load would cost a fortune. Ten databases in an elastic pool share a common resource ceiling — one spikes while others idle.
Azure Database for PostgreSQL — Flexible Server
If your team thinks in PostgreSQL and you want a managed service that actually feels like PostgreSQL, Flexible Server is the answer. It replaced the older Single Server offering and gives you full control over PostgreSQL extensions, parameters, and versions.
# Create a PostgreSQL Flexible Server
az postgres flexible-server create \
--resource-group rg-database-demo \
--name pg-flex-prod-2025 \
--location eastus \
--admin-user pgadmin \
--admin-password 'P0stgres$ecure!' \
--sku-name Standard_D2ds_v4 \
--tier GeneralPurpose \
--storage-size 128 \
--version 16 \
--high-availability ZoneRedundant
The connection string follows standard PostgreSQL format:
# Connect using psql
psql "host=pg-flex-prod-2025.postgres.database.azure.com \
port=5432 \
dbname=postgres \
user=pgadmin \
sslmode=require"
Cosmos DB — Multi-Model Global Distribution
Cosmos DB is Azure's globally distributed, multi-model database. It handles documents (NoSQL API), graph (Gremlin), key-value (Table), column-family (Cassandra), and even PostgreSQL (via the PostgreSQL API). But its sweet spot is the NoSQL API for JSON document workloads with global reach.
Consistency Levels
This is what makes Cosmos DB unique. Instead of forcing you into "strong or eventual," it offers five tunable consistency levels:
| Level | Guarantee | Latency | Use Case |
|---|---|---|---|
| Strong | Linearizable reads | Highest | Financial transactions |
| Bounded Staleness | Reads lag by at most K versions or T seconds | High | Leaderboards, inventory counts |
| Session | Read-your-own-writes within a session | Medium | User profiles, shopping carts |
| Consistent Prefix | Reads never see out-of-order writes | Low | Social feeds, activity logs |
| Eventual | No ordering guarantee | Lowest | Analytics, logging |
Session consistency is the default and the right choice for 90% of applications.
Creating a Cosmos DB Account
# Create a Cosmos DB account (NoSQL API)
az cosmosdb create \
--resource-group rg-database-demo \
--name cosmos-prod-2025 \
--kind GlobalDocumentDB \
--locations regionName=eastus failoverPriority=0 \
--locations regionName=westeurope failoverPriority=1 \
--default-consistency-level Session \
--enable-automatic-failover true
# Create a database
az cosmosdb sql database create \
--resource-group rg-database-demo \
--account-name cosmos-prod-2025 \
--name OrdersDB
# Create a container with partition key
az cosmosdb sql container create \
--resource-group rg-database-demo \
--account-name cosmos-prod-2025 \
--database-name OrdersDB \
--name Orders \
--partition-key-path /customerId \
--throughput 400
Provisioned vs Serverless Throughput
Cosmos DB charges for Request Units (RU/s). You have two options:
- Provisioned throughput: You set RU/s upfront (400-1,000,000). Best for production with predictable traffic.
- Serverless: Pay per operation. Best for dev/test or sporadic workloads.
The Comparison Table
| Feature | Azure SQL Database | PostgreSQL Flexible Server | Cosmos DB |
|---|---|---|---|
| Type | Relational (SQL Server) | Relational (PostgreSQL) | Multi-model (NoSQL primary) |
| Best For | Enterprise apps, ERP, .NET stacks | Open-source stacks, analytics | Global apps, IoT, real-time |
| Scaling | Vertical (DTU/vCore) + Read replicas | Vertical + Read replicas | Horizontal (partitioning) + Global |
| Global Distribution | Geo-replication (async) | Read replicas (limited regions) | Multi-region writes (native) |
| Consistency | Strong (ACID) | Strong (ACID) | 5 tunable levels |
| Pricing Model | DTU or vCore | Compute + Storage | RU/s + Storage |
| Starting Cost | ~$5/month (Basic DTU) | ~$12/month (Burstable B1ms) | Free tier (1000 RU/s) |
| Max Storage | 4 TB (100 TB Hyperscale) | 16 TB | Unlimited |
| Backup | Automated (7-35 days) | Automated (7-35 days) | Continuous (point-in-time) |
Backup and Restore
Every database needs a recovery story. Here is how each service handles it:
# Azure SQL — restore to a point in time
az sql db restore \
--resource-group rg-database-demo \
--server sql-server-prod-2025 \
--name db-inventory-restored \
--dest-name db-inventory-restored \
--time "2025-05-09T10:00:00Z"
# PostgreSQL — restore to a point in time
az postgres flexible-server restore \
--resource-group rg-database-demo \
--name pg-flex-restored \
--source-server pg-flex-prod-2025 \
--restore-time "2025-05-09T10:00:00Z"
# Cosmos DB — continuous backup is automatic
# Restore to a point in time (creates a new account)
az cosmosdb restore \
--resource-group rg-database-demo \
--account-name cosmos-prod-2025 \
--target-database-account-name cosmos-restored-2025 \
--restore-timestamp "2025-05-09T10:00:00Z" \
--location eastus
Wrapping Up
The right database is the one that matches your data model, scale requirements, and team skills. Azure SQL Database is the safe choice for relational workloads in Microsoft-centric environments. PostgreSQL Flexible Server is the go-to for open-source teams who want managed infrastructure without vendor lock-in. Cosmos DB is the right answer when you need global distribution, massive throughput, or flexible consistency — but it demands careful partition key design and RU planning. Pick one, learn it deeply, and remember: you can always add a second database for a different workload later.
Next up: We will dive into Azure Functions — building serverless compute that scales to zero and triggers from almost anything.
