Choosing the Right Azure Storage Service
Azure offers seven distinct storage services, each optimized for a different data type and access pattern. Picking the wrong one is expensive to fix later. This page cuts through the options with concrete use cases, a comparison table, and a decision checklist you can apply to your next project.
The seven storage services at a glance
| Service | Data model | Best for | Not designed for |
|---|---|---|---|
| Blob Storage | Unstructured binary objects | Images, videos, backups, files | Querying content, relational data |
| Azure Files | SMB/NFS file shares | Shared config, lift-and-shift apps | High-IOPS databases, analytics |
| Azure Queues | FIFO message queue | Async job processing, decoupling | Pub/sub, ordered delivery guarantees |
| Azure Table Storage | Key-value / wide-column | Simple lookups, telemetry at low cost | Complex queries, global distribution |
| Cosmos DB | Document / key-value / column / graph | Global scale, flexible schema, high throughput | Complex multi-table JOINs, ad-hoc SQL |
| ADLS Gen2 | Hierarchical object storage | Analytics, data lakes, Spark/Synapse | Transactional workloads, small file access |
| Azure SQL Database | Relational (SQL Server) | Relational data, complex queries, ACID | Unstructured data, horizontal sharding |
Blob Storage: unstructured objects at scale
Use Blob Storage when you are storing and serving files where the content is opaque to the storage layer — images, videos, PDFs, audio files, application backups, log archives, and static website assets. Blob Storage scales to petabytes with no configuration and serves files over HTTP/HTTPS directly from the storage endpoint.
Example: storing product images for an e-commerce site. Product images are uploaded by sellers, stored in Blob containers organized by product ID, and served via CDN URLs. The application stores only the blob name in the relational database, not the image bytes. Lifecycle management policies move images for discontinued products to the Cool or Archive tier automatically to reduce costs.
Blob Storage is not the right choice when you need to query the file content (full-text search, SQL queries), manage structured metadata alongside files, or serve files at sub-millisecond latency from an application’s local filesystem.
Azure Files: shared file systems for VMs and containers
Use Azure Files when you need a network file share that multiple VMs or containers can mount simultaneously using SMB (Windows, Linux) or NFS (Linux). It is particularly useful for lift-and-shift migrations where an on-premises application reads configuration from a network share, or for Kubernetes workloads that need a ReadWriteMany persistent volume.
Example: shared configuration files across multiple App Service instances or VMs. A legacy application reads its configuration from \fileserver\config\app.ini. In Azure, you create an Azure Files share, mount it on each VM, and point the application at the same path. No code change required.
Azure Files has throughput and IOPS limits that make it unsuitable for database files (SQL Server data files on Azure Files will be slow) or high-frequency small-file access (each file operation has network latency). For database workloads, use Azure Managed Disks. For analytics, use Blob Storage or ADLS Gen2.
Azure Queues: lightweight async messaging
Use Azure Queues (part of Azure Storage) for simple first-in-first-out message queuing between application components. A producer writes messages and a consumer polls the queue, processes messages, and deletes them. Messages are held for up to 7 days and can be up to 64 KB in size.
Example: order processing pipeline. A web application accepts an order, writes the order ID to a Storage Queue, and returns a response to the user immediately. A background Azure Function polls the queue, processes the order (inventory check, payment, fulfillment), and deletes the message when complete. The web application and the processing function are fully decoupled — if processing is slow, messages queue up without affecting the user experience.
Use Azure Service Bus instead of Storage Queues when you need dead-letter queues, ordered delivery, message sessions, pub/sub topics, or messages larger than 64 KB. Storage Queues are the simple, low-cost option; Service Bus adds reliability features at higher cost.
Cosmos DB: global-scale NoSQL
Use Cosmos DB when your data is document-shaped, the schema evolves frequently, you need multiple global write regions, or you need sustained very high throughput (tens of thousands of operations per second). Cosmos DB also excels at session state, user profiles, product catalogs, gaming leaderboards, and any workload where you look up data by a single key.
Example: user session state for a global SaaS application. Users from Europe, Asia, and North America all expect fast responses. Cosmos DB with multi-region writes stores session data in the region closest to each user. Read and write latency is under 10ms in each region. The schema is flexible — different sessions can have different fields. TTL automatically expires sessions after 24 hours of inactivity.
Cosmos DB is not the right choice for complex analytical queries that join data across multiple collections, reporting workloads that aggregate across millions of records on non-partition-key fields, or applications that have many-to-many relational constraints enforced at the database level. For those, use Azure SQL.
Cosmos DB charges for Request Units (RUs), not just storage. A workload that reads 1 million small documents per day consumes roughly 1 million RUs. At the default minimum provisioned throughput of 400 RU/s, the monthly bill for the throughput alone is around $23. For very low-traffic workloads, serverless Cosmos DB (billing per request) is often cheaper.
ADLS Gen2: the analytics data lake
Use ADLS Gen2 as the storage layer for big data analytics, machine learning feature stores, and data warehouse landing zones. It integrates natively with Azure Synapse Analytics, Azure Databricks, Azure Data Factory, and HDInsight. The hierarchical namespace enables data lake zoning (raw/cleansed/curated) with per-directory ACL controls for different teams.
Example: enterprise data lake for reporting and ML. Data from CRM, ERP, and web analytics lands in the raw zone as JSON and CSV files via Data Factory pipelines. A Synapse Spark pool reads the raw data, validates and enriches it, and writes Parquet files to the cleansed zone. Data science teams read from the curated zone using Databricks notebooks. ADLS Gen2 ACLs give each team read/write access only to their own zone.
ADLS Gen2 is not suitable for transactional reads and writes (it has no row-level locking, no ACID transactions), low-latency random access to small files, or serving content over HTTP to end users (use Blob Storage with CDN for that).
Azure SQL Database: relational data with managed infrastructure
Use Azure SQL Database when your data is relational — multiple tables with foreign key relationships, complex queries with multi-table JOINs, or application logic that depends on ACID transactions across multiple rows or tables. It runs SQL Server on fully managed infrastructure with automatic backups, patching, and high availability.
Example: order management system for a retail application. Orders, customers, products, inventory, and shipping records all have relationships. A single order creation transaction inserts a row in the orders table, updates inventory, and creates a shipping record — all atomically. Reporting queries join orders with customers and products to calculate revenue by category. Azure SQL handles all of this natively with standard SQL.
Azure SQL has limits at the high end: a single database scales to 4 TB of storage and thousands of IOPS, but it is a single-instance model (not horizontally sharded). For write workloads that exceed what a single SQL instance can handle, consider sharding with Elastic Database Pools, or rearchitecting with Cosmos DB for the write-heavy entities.
Quick-decision checklist
Work through these questions to narrow down the right service:
- Are you storing files (images, PDFs, videos, binaries)? → Blob Storage. If multiple VMs need to mount it as a drive → Azure Files.
- Do you need to decouple components asynchronously? → Azure Queues (simple) or Azure Service Bus (advanced).
- Is your data relational with JOINs, foreign keys, and complex queries? → Azure SQL Database (or PostgreSQL/MySQL Flexible Server for open-source engines).
- Is your data JSON documents with a flexible or evolving schema? → Cosmos DB.
- Do you need global multi-region writes? → Cosmos DB (only Azure NoSQL option with multi-master writes).
- Are you building an analytics pipeline or data lake? → ADLS Gen2 as the storage layer, with Synapse or Databricks for compute.
- Do you need a simple key-value store at very low cost? → Azure Table Storage (or Cosmos DB Table API if you want better performance).
- Is cost the primary constraint and data volume is very high? → Blob Storage (cheapest per-GB) or ADLS Gen2 with lifecycle tiering to Cool/Archive.
Cost comparison guidance
Storage costs vary widely across services. These are approximate figures for the East US region to give order-of-magnitude comparisons:
| Service | Storage cost | Request/compute cost |
|---|---|---|
| Blob Storage (Hot) | ~$0.018/GB/month | ~$0.004 per 10,000 operations |
| Blob Storage (Cool) | ~$0.01/GB/month | Higher per-operation cost + retrieval fee |
| Azure Files (Standard) | ~$0.06/GB/month | Included in storage price |
| Cosmos DB (provisioned) | ~$0.25/GB/month | ~$0.008 per 100 RU/s per hour |
| Azure SQL (General Purpose) | ~$0.115/GB/month | ~$0.17/vCore/hour |
| ADLS Gen2 (Hot) | ~$0.018/GB/month | ~$0.004 per 10,000 operations |
| Azure Table Storage | ~$0.045/GB/month | ~$0.000036 per 10,000 transactions |
The biggest cost trap is mismatching the service to the workload. A Cosmos DB container provisioned at 10,000 RU/s costs around $60/day whether or not you use those RUs. A Blob Storage account holding 1 TB costs roughly $18/month. Put binary files in Blob Storage, not Cosmos DB documents.
Common mistakes
- Storing binary files (images, PDFs) as base64 strings inside Cosmos DB documents. Cosmos DB charges per RU based on the size of the item read or written. A 500 KB base64-encoded image stored as a document field costs hundreds of RUs per read — vs a few cents per GB-month in Blob Storage. Store files in Blob Storage, store the URL reference in Cosmos DB, and serve the file directly from the Blob endpoint.
- Using Azure SQL for a workload that actually needs horizontal scaling. Azure SQL scales vertically (bigger vCores, more memory) but not horizontally across multiple write nodes. If your write rate exceeds what a single SQL instance can handle at a reasonable price point, the answer is not “use a bigger SQL tier” — it is to rearchitect with a service designed for horizontal writes, like Cosmos DB or a sharded approach.
- Using Blob Storage for a data lake without enabling hierarchical namespace. Flat-namespace Blob Storage works functionally with analytics tools but has O(n) directory operations. A Databricks job that renames a staging directory to a final directory after processing touches every blob in that path one-by-one. On a 1-million-file partition, this takes minutes. ADLS Gen2 hierarchical namespace makes the same rename operation atomic and instantaneous. Enable it from the start.
Summary
- Blob Storage is for binary files at low cost; Azure Files is for SMB/NFS shares; Azure Queues is for simple async decoupling between components.
- Cosmos DB handles document-shaped data with high throughput and global multi-region writes; Azure SQL handles relational data with complex JOINs and ACID transactions.
- ADLS Gen2 is the correct storage layer for analytics pipelines and data lakes — Blob Storage with hierarchical namespace enabled for efficient directory operations.
- Most production applications use multiple storage services together, matching each service to the data type and access pattern it is optimized for.
Frequently asked questions
Can I use multiple storage services in the same application?
Yes, and most production applications do. A typical web application might use Azure SQL for relational user and order data, Blob Storage for images and documents, Azure Cache for Redis for session state, and Azure Queues for background job processing. Using the right service for each data type rather than forcing everything into one store is a core Azure architecture principle.
When should I use Cosmos DB instead of Azure SQL?
Use Cosmos DB when you need global multi-region writes, a flexible document schema that changes frequently, extremely high throughput (tens of thousands of writes per second), or when your data is naturally hierarchical JSON rather than relational. Use Azure SQL when you have complex relational queries, JOINs across many tables, strong ACID transaction requirements across multiple entities, or when your team is most comfortable with SQL and a fixed schema.
Is Azure Table Storage still relevant when Cosmos DB exists?
Table Storage is a low-cost option for simple key-value lookups at very large scale. Cosmos DB's Table API is a drop-in replacement with better performance and global distribution. For new workloads, prefer Cosmos DB Table API over Azure Table Storage — the pricing difference is small and the operational flexibility is much greater. Table Storage is primarily relevant for legacy workloads already using it.