Azure Cache for Redis Guide
Implement caching with Azure Cache for Redis, covering tiers, caching patterns, session management, clustering, geo-replication, and security.
Prerequisites
- Basic understanding of caching concepts and patterns
- Familiarity with Azure networking
- Experience with application development
Introduction to Azure Cache for Redis
Azure Cache for Redis is a fully managed, in-memory data store based on the open-source Redis engine. It provides sub-millisecond data access for applications that require extremely fast read and write operations, making it one of the most important building blocks in high-performance cloud architectures. Common use cases include caching frequently accessed database queries, managing user sessions, real-time leaderboards, pub/sub messaging, rate limiting, and distributed locking.
The value proposition of a managed Redis service is significant: Azure handles patching, replication, failover, monitoring, and scaling. These are operational tasks that are complex and error-prone when managing Redis yourself. Azure Cache for Redis also integrates natively with Azure Virtual Networks for security isolation, Azure Monitor for observability, and Microsoft Entra ID for authentication, providing a production-grade caching layer with minimal operational overhead.
This guide covers everything from tier selection and initial setup through advanced topics like clustering, geo-replication, persistence, and security. Whether you are adding caching to an existing application or designing a new system that requires high-speed data access, this guide provides the practical knowledge you need.
Redis Stack on Enterprise Tier
The Azure Cache for Redis Enterprise tier includes Redis Stack, which adds powerful modules beyond core Redis: RediSearch (full-text search and secondary indexing), RedisJSON (native JSON document support), RedisTimeSeries (time-series data), and RedisBloom (probabilistic data structures). If your application needs search, JSON manipulation, or time-series capabilities alongside caching, the Enterprise tier with Redis Stack eliminates the need for separate services.
Tiers & SKU Comparison
Azure Cache for Redis offers four tiers, each targeting different performance, scale, and feature requirements. Choosing the right tier impacts cost, performance, availability, and the features available to your application. The following comparison covers all four tiers with their key differentiators.
| Feature | Basic | Standard | Premium | Enterprise |
|---|---|---|---|---|
| SLA | No SLA | 99.9% | 99.9% | 99.99% |
| Replication | None | Primary/replica | Primary/replica | Active-active geo-replication |
| Max Cache Size | 53 GB | 53 GB | 120 GB (1.2 TB clustered) | Up to several TB |
| Clustering | No | No | Yes (up to 10 shards) | Yes (up to 500+ shards) |
| Data Persistence | No | No | RDB/AOF | RDB/AOF |
| VNet Support | No | No | Yes | Yes |
| Zone Redundancy | No | No | Yes | Yes |
| Geo-Replication | No | No | Passive (one-way) | Active (multi-write) |
| Redis Modules | No | No | No | Yes (RediSearch, RedisJSON, etc.) |
| Best For | Dev/test only | Small production workloads | Production with persistence & VNet | Mission-critical, global apps |
Basic Tier Limitations
The Basic tier runs on a single VM with no replication, no SLA, and no data persistence. If the VM fails, all cached data is lost and the service is unavailable until Azure replaces the instance. Never use the Basic tier for production workloads. It exists solely for development, testing, and prototyping. Even for non-critical production caches, use at minimum the Standard tier for its primary/replica replication and 99.9% SLA.
Setting Up Azure Cache for Redis
Creating an Azure Cache for Redis instance involves selecting the tier, cache size, region, and networking configuration. The cache name must be globally unique (it becomes the hostname: <name>.redis.cache.windows.net). After creation, you configure your application to connect using either connection strings or Microsoft Entra ID authentication.
# Create a Premium tier cache with clustering
az redis create \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--location eastus \
--sku Premium \
--vm-size P1 \
--shard-count 2 \
--enable-non-ssl-port false \
--minimum-tls-version 1.2 \
--redis-version 6 \
--tags project=myapp environment=production
# Wait for provisioning to complete (can take 15-30 minutes for Premium)
az redis show \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--query '{Name:name, Status:provisioningState, Host:hostName, Port:sslPort, Shards:shardCount}' \
--output table
# Retrieve access keys
az redis list-keys \
--resource-group rg-cache-prod \
--name redis-myapp-prod
# Configure firewall rules (if not using VNet)
az redis firewall-rules create \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--rule-name AllowAppService \
--start-ip 10.0.1.0 \
--end-ip 10.0.1.255
# Enable Microsoft Entra ID authentication (recommended)
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--set redisConfiguration.aad-enabled=true
# Configure diagnostic settings
az monitor diagnostic-settings create \
--resource /subscriptions/<sub-id>/resourceGroups/rg-cache-prod/providers/Microsoft.Cache/redis/redis-myapp-prod \
--name redis-diagnostics \
--workspace /subscriptions/<sub-id>/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/law-central \
--metrics '[{"category": "AllMetrics", "enabled": true}]'Connecting from Application Code
using StackExchange.Redis;
using Microsoft.Extensions.Caching.StackExchangeRedis;
var builder = WebApplication.CreateBuilder(args);
// Option 1: StackExchange.Redis with connection multiplexer
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
{
var config = ConfigurationOptions.Parse(
builder.Configuration.GetConnectionString("Redis")!);
config.AbortOnConnectFail = false; // Don't crash on startup if Redis is down
config.ConnectRetry = 3; // Retry connection attempts
config.ConnectTimeout = 5000; // 5 second connection timeout
config.SyncTimeout = 3000; // 3 second operation timeout
config.ReconnectRetryPolicy = new ExponentialRetry(5000);
return ConnectionMultiplexer.Connect(config);
});
// Option 2: IDistributedCache abstraction (simpler API)
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "myapp:"; // Key prefix for namespace isolation
});
var app = builder.Build();
// Example: Using IConnectionMultiplexer directly
app.MapGet("/api/products/{id}", async (string id, IConnectionMultiplexer redis) =>
{
var db = redis.GetDatabase();
var cacheKey = $"product:{id}";
// Try cache first
var cached = await db.StringGetAsync(cacheKey);
if (cached.HasValue)
{
return Results.Ok(JsonSerializer.Deserialize<Product>(cached!));
}
// Cache miss: fetch from database
var product = await GetProductFromDatabase(id);
if (product == null) return Results.NotFound();
// Store in cache with 10-minute expiry
await db.StringSetAsync(cacheKey, JsonSerializer.Serialize(product),
TimeSpan.FromMinutes(10));
return Results.Ok(product);
});
app.Run();Caching Patterns & Strategies
Simply adding a cache does not automatically improve performance. You need to implement the right caching pattern for your specific use case. Each pattern has different trade-offs regarding data consistency, cache hit ratio, latency, and complexity. Understanding these patterns is essential for effective cache design.
Common Caching Patterns
| Pattern | How It Works | Consistency | Best For |
|---|---|---|---|
| Cache-Aside | Application checks cache; on miss, loads from DB and populates cache | Eventual (cache may serve stale data until TTL expires) | Most read-heavy workloads (most common pattern) |
| Write-Through | Application writes to cache and DB simultaneously | Strong (cache always in sync) | Workloads where data must always be fresh |
| Write-Behind | Application writes to cache; cache asynchronously writes to DB | Eventual (DB lags behind cache) | Write-heavy workloads needing low write latency |
| Read-Through | Cache itself loads data from DB on miss (cache acts as data source) | Eventual | Simplifies application code; cache is the single data interface |
| Refresh-Ahead | Cache proactively refreshes entries before they expire | Near-real-time | Hot data that must never have a cache miss |
public class CacheService
{
private readonly IDatabase _redis;
private readonly ILogger<CacheService> _logger;
// Cache-aside with stampede protection using Redis SETNX
public async Task<T?> GetOrSetAsync<T>(
string key,
Func<Task<T>> factory,
TimeSpan expiry,
TimeSpan lockTimeout = default)
{
lockTimeout = lockTimeout == default ? TimeSpan.FromSeconds(10) : lockTimeout;
// 1. Try to get from cache
var cached = await _redis.StringGetAsync(key);
if (cached.HasValue)
{
return JsonSerializer.Deserialize<T>(cached!);
}
// 2. Cache miss - acquire a distributed lock to prevent stampede
var lockKey = $"lock:{key}";
var lockValue = Guid.NewGuid().ToString();
var acquired = await _redis.StringSetAsync(lockKey, lockValue,
lockTimeout, When.NotExists);
if (!acquired)
{
// Another thread is populating the cache; wait and retry
await Task.Delay(100);
cached = await _redis.StringGetAsync(key);
if (cached.HasValue)
return JsonSerializer.Deserialize<T>(cached!);
// Still nothing; fall through to factory
}
try
{
// 3. Load from the authoritative data source
var value = await factory();
if (value == null) return default;
// 4. Populate cache with a TTL
var serialized = JsonSerializer.Serialize(value);
await _redis.StringSetAsync(key, serialized, expiry);
return value;
}
finally
{
// 5. Release the lock (only if we still own it)
var script = @"
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end";
await _redis.ScriptEvaluateAsync(script,
new RedisKey[] { lockKey },
new RedisValue[] { lockValue });
}
}
// Cache invalidation on write operations
public async Task InvalidateAsync(params string[] keys)
{
var redisKeys = keys.Select(k => (RedisKey)k).ToArray();
await _redis.KeyDeleteAsync(redisKeys);
_logger.LogInformation("Invalidated {Count} cache keys: {Keys}",
keys.Length, string.Join(", ", keys));
}
}Cache Stampede Prevention
A cache stampede (also called thundering herd) occurs when a popular cache entry expires and many concurrent requests simultaneously attempt to regenerate it, overwhelming the database. The distributed lock pattern shown above prevents this by ensuring only one request populates the cache while others wait. An alternative approach is probabilistic early recomputation, where the cache entry is refreshed before expiry based on a probability function, which avoids the lock overhead entirely.
Session State Management
Redis is an excellent store for web application session state because it provides fast read/write access and data persistence beyond the lifetime of a single application instance. By storing sessions in Redis instead of in-process memory, you enable horizontal scaling (multiple app instances share the same session store) and resilience (sessions survive application restarts and deployments).
var builder = WebApplication.CreateBuilder(args);
// Configure Redis as the distributed session store
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "session:";
});
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
});
var app = builder.Build();
app.UseSession();
// Example: Store and retrieve session data
app.MapPost("/api/cart/add", async (HttpContext context, CartItem item) =>
{
var cart = context.Session.GetString("cart");
var items = cart != null
? JsonSerializer.Deserialize<List<CartItem>>(cart)!
: new List<CartItem>();
items.Add(item);
context.Session.SetString("cart", JsonSerializer.Serialize(items));
return Results.Ok(new { ItemCount = items.Count });
});
app.MapGet("/api/cart", async (HttpContext context) =>
{
var cart = context.Session.GetString("cart");
var items = cart != null
? JsonSerializer.Deserialize<List<CartItem>>(cart)!
: new List<CartItem>();
return Results.Ok(items);
});Data Persistence & Backup
By default, Redis is an in-memory store; if the Redis process crashes, all data is lost. Azure Cache for Redis Premium and Enterprise tiers support data persistence, which periodically saves in-memory data to Azure Storage. This means your cache can survive process restarts, patching, and failover events without losing data.
Persistence Options
| Method | How It Works | RPO | Performance Impact |
|---|---|---|---|
| RDB (Snapshots) | Periodic point-in-time snapshots of the dataset | Minutes (depending on schedule) | Low (snapshot in background process) |
| AOF (Append-Only File) | Logs every write operation; replayed on restart | Seconds (every write is logged) | Medium (every write has disk I/O) |
| RDB + AOF | Combines both for maximum durability | Seconds | Medium |
# Enable RDB persistence (snapshot every 15 minutes)
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--set redisConfiguration.rdb-backup-enabled=true \
redisConfiguration.rdb-backup-frequency=15 \
redisConfiguration.rdb-storage-connection-string="<storage-connection-string>"
# Enable AOF persistence for minimum data loss
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--set redisConfiguration.aof-enabled=true \
redisConfiguration.aof-storage-connection-string-0="<storage-connection-string>"
# Export a snapshot manually (for backup before major changes)
az redis export \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--prefix backup-20240115 \
--container "https://stredisbackup.blob.core.windows.net/backups" \
--file-format rdb
# Import a snapshot (for restoring data)
az redis import \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--files "https://stredisbackup.blob.core.windows.net/backups/backup-20240115.rdb" \
--file-format rdbPersistence vs Caching
Data persistence in Redis is designed for warm-start recovery (so the cache is populated after a restart), not as a substitute for a durable database. Even with AOF persistence, there is a small window of potential data loss. If your application relies on Redis as the primary data store (not just a cache), consider the Enterprise tier with active geo-replication for higher durability, and always maintain a separate authoritative database for critical data.
Clustering & Scaling
Redis clustering distributes your data across multiple Redis shards (each shard is a primary/replica pair), enabling you to scale beyond the memory and throughput limits of a single node. Azure Cache for Redis Premium tier supports up to 10 shards, and the Enterprise tier supports much larger clusters. Clustering is transparent to clients using modern Redis libraries, which handle key-to-shard routing automatically.
Scaling Options
| Scaling Type | How It Works | When to Use |
|---|---|---|
| Scale Up | Move to a larger cache size (e.g., C1 to C3, P1 to P4) | Need more memory or CPU on a single node |
| Scale Out (Cluster) | Add more shards to distribute data and load | Need more total memory or throughput beyond one node |
| Read Replicas | Add read-only replicas to offload read traffic | Read-heavy workloads where writes are infrequent |
# Scale up: change the VM size (Premium tier)
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--sku Premium \
--vm-size P2
# Scale out: add more shards to the cluster
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--shard-count 4
# Scale in: reduce shards (data will be rebalanced)
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--shard-count 2
# Check cluster info
az redis show \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--query '{Name:name, Shards:shardCount, Size:sku.name, Family:sku.family, Capacity:sku.capacity}' \
--output tableGeo-Replication & High Availability
For mission-critical applications that require cache availability across regions, Azure Cache for Redis supports geo-replication. The Premium tier offers passive geo-replication (one-way, read-only secondary), while the Enterprise tier offers active geo-replication (multi-master, read/write in all regions). The type of geo-replication available depends on your tier and significantly affects your application architecture.
Geo-Replication Comparison
| Feature | Passive (Premium) | Active (Enterprise) |
|---|---|---|
| Direction | One-way (primary to secondary) | Multi-directional (all nodes read/write) |
| Secondary Reads | Read-only | Full read/write |
| Failover | Manual (unlink geo-replication, reconfigure app) | Automatic (surviving instances continue working) |
| Conflict Resolution | N/A (single writer) | Last-write-wins with CRDTs |
| Latency | Asynchronous replication lag | Asynchronous with conflict resolution |
# Create primary cache in East US (already exists)
# Create secondary cache in West US
az redis create \
--resource-group rg-cache-dr \
--name redis-myapp-westus \
--location westus \
--sku Premium \
--vm-size P1 \
--shard-count 2
# Link the caches for geo-replication
az redis server-link create \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--server-to-link /subscriptions/<sub-id>/resourceGroups/rg-cache-dr/providers/Microsoft.Cache/redis/redis-myapp-westus \
--replication-role Secondary
# Check replication status
az redis server-link list \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--output table
# Failover: unlink geo-replication (promotes secondary to independent primary)
# WARNING: This is a manual process and breaks the replication link
az redis server-link delete \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--linked-server-name redis-myapp-westusSecurity & Network Isolation
Securing your Redis cache is critical because it often stores sensitive data including user sessions, authentication tokens, and cached business data. Azure Cache for Redis provides multiple security layers: authentication (access keys or Microsoft Entra ID), encryption (TLS in transit, encryption at rest), and network isolation (VNet integration, private endpoints, firewall rules).
Security Best Practices
- Use Microsoft Entra ID authentication: Instead of shared access keys, configure Entra ID authentication with RBAC roles (
Redis Cache Contributorfor management, custom data access roles for applications). This eliminates key rotation concerns and provides identity-based audit trails. - Enforce TLS 1.2 minimum: Disable non-SSL port (6379) and set
minimum-tls-versionto 1.2 to prevent downgrade attacks. - Use Private Endpoints: Deploy Redis with a private endpoint to ensure traffic never traverses the public internet. This is the recommended networking approach for production caches.
- Disable public network access: Once private endpoints are configured, disable public network access entirely.
- Rotate access keys regularly: If using access key authentication, rotate keys on a regular schedule. Use the secondary key during rotation to avoid downtime.
# Disable non-SSL port and enforce TLS 1.2
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--set enableNonSslPort=false \
minimumTlsVersion=1.2
# Create a private endpoint for the Redis cache
az network private-endpoint create \
--resource-group rg-cache-prod \
--name pe-redis-myapp \
--vnet-name vnet-app-prod \
--subnet snet-private-endpoints \
--private-connection-resource-id /subscriptions/<sub-id>/resourceGroups/rg-cache-prod/providers/Microsoft.Cache/redis/redis-myapp-prod \
--group-id redisCache \
--connection-name pec-redis
# Disable public network access
az redis update \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--set publicNetworkAccess=Disabled
# Regenerate access keys (rotate primary key)
az redis regenerate-key \
--resource-group rg-cache-prod \
--name redis-myapp-prod \
--key-type PrimaryRedis Data Encryption
Azure Cache for Redis encrypts data in transit using TLS by default. For data at rest, encryption is automatically enabled for Premium and Enterprise tiers using Microsoft-managed keys. The Enterprise tier also supports customer-managed keys (CMK) stored in Azure Key Vault for organizations that require full control over encryption keys for compliance purposes.
Monitoring & Best Practices
Effective Redis monitoring requires tracking both standard infrastructure metrics and Redis-specific operational metrics. Azure Monitor provides built-in metrics for Azure Cache for Redis, and you can set up alerts for conditions that indicate performance degradation, capacity issues, or operational problems.
Key Metrics to Monitor
| Metric | Healthy Range | Alert When |
|---|---|---|
| Server Load | < 70% | > 80% sustained (scale up needed) |
| Used Memory % | < 80% | > 90% (evictions likely, scale up/out) |
| Cache Hit Rate | > 90% | < 80% (review caching patterns) |
| Connected Clients | Stable | Sudden spikes or drops (connection leak or outage) |
| Evicted Keys | 0 | > 0 sustained (cache too small for workload) |
| Operations/sec | Varies by workload | Sudden changes (traffic anomaly or issue) |
| Cache Latency | < 1 ms | > 5 ms (performance degradation) |
# Query key Redis metrics
az monitor metrics list \
--resource /subscriptions/<sub-id>/resourceGroups/rg-cache-prod/providers/Microsoft.Cache/redis/redis-myapp-prod \
--metric "percentProcessorTime" "usedmemorypercentage" "cacheHits" "cacheMisses" "evictedkeys" "connectedclients" \
--aggregation Average Maximum \
--interval PT5M \
--output table
# Create alerts for critical Redis metrics
az monitor metrics alert create \
--resource-group rg-cache-prod \
--name "alert-redis-high-memory" \
--description "Redis memory usage exceeds 90%" \
--scopes /subscriptions/<sub-id>/resourceGroups/rg-cache-prod/providers/Microsoft.Cache/redis/redis-myapp-prod \
--condition "avg usedmemorypercentage > 90" \
--window-size 5m \
--evaluation-frequency 1m \
--severity 1 \
--action ag-platform-team
az monitor metrics alert create \
--resource-group rg-cache-prod \
--name "alert-redis-high-server-load" \
--description "Redis server load exceeds 80%" \
--scopes /subscriptions/<sub-id>/resourceGroups/rg-cache-prod/providers/Microsoft.Cache/redis/redis-myapp-prod \
--condition "avg percentProcessorTime > 80" \
--window-size 5m \
--evaluation-frequency 1m \
--severity 2 \
--action ag-platform-teamBest Practices Summary
- Choose the right tier from the start: Migrating between tiers requires creating a new cache and migrating data. Plan for production requirements (persistence, VNet, clustering) early.
- Set appropriate TTLs on all keys: Never cache data without a TTL. Unbounded caches grow until they hit memory limits and trigger evictions, which can degrade performance unpredictably.
- Use key namespacing: Prefix all keys with the application or service name (e.g.,
myapp:users:12345) to prevent key collisions when multiple services share a cache. - Implement graceful degradation: Your application should continue to function (perhaps with degraded performance) if Redis is unavailable. Use try-catch around cache operations and fall back to the database.
- Avoid large keys: Keep individual values under 100 KB. Large values consume memory disproportionately and can cause latency spikes. Use compression for larger payloads or break them into multiple keys.
- Use connection pooling: Reuse Redis connections through a singleton
ConnectionMultiplexer(in .NET) or equivalent connection pool in other languages. Creating new connections per request causes significant overhead. - Monitor eviction policies: Understand your eviction policy (
volatile-lru,allkeys-lru,noeviction) and how it affects your application when memory pressure occurs.
Key Takeaways
- 1Azure Cache for Redis offers Basic, Standard, Premium, and Enterprise tiers for different workloads.
- 2Cache-aside, write-through, and write-behind patterns suit different consistency requirements.
- 3Session state management with Redis scales web applications across multiple instances.
- 4Premium tier supports clustering (up to 10 shards), persistence, and VNet integration.
- 5Enterprise tier adds RediSearch, RedisBloom, and RedisTimeSeries modules.
- 6Geo-replication enables active-passive caching for multi-region applications.
Frequently Asked Questions
Which Azure Cache for Redis tier should I choose?
How does clustering work?
What is the difference between active geo-replication and passive?
How do I secure Azure Cache for Redis?
How much does Azure Cache for Redis cost?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.