Government applications that serve residents - permit portals, benefits eligibility checkers, GIS lookups, licensing systems - share a common performance bottleneck: repeated database queries for data that changes infrequently. A well-designed caching layer can cut response times by 50-90% and dramatically reduce database load. While Redis is the go-to caching solution for many, not every agency wants to introduce a new service into their architecture. If you are already running Azure Database for PostgreSQL, you can now use it as a distributed cache backing store with first-class .NET support.
Microsoft’s Microsoft.Extensions.Caching.Postgres NuGet package implements the IDistributedCache interface using PostgreSQL as the storage engine. This means you can add a distributed caching layer to your .NET applications without provisioning a separate caching service, while still using the same abstraction that works with Redis, SQL Server, or Cosmos DB.
Architecture Overview
The architecture is straightforward: your .NET application uses the standard IDistributedCache interface, which routes cache reads and writes to a dedicated table in your PostgreSQL database. The PostgreSQL Flexible Server handles storage, expiration cleanup, and high availability.
Key architectural benefits:
- Single data platform: No additional service to manage, monitor, or secure
- Built-in HA: Leverage PostgreSQL Flexible Server’s zone-redundant high availability (99.99% SLA)
- Connection pooling: Built-in PgBouncer handles connection management at scale
- Microsoft Entra authentication: No passwords to rotate; use managed identities
- FedRAMP High: Azure Database for PostgreSQL is available in Azure Government regions
This pattern works best for:
- Session state in multi-instance web applications
- Caching API responses from upstream services
- Storing computed results (eligibility calculations, report aggregations)
- Rate limiting and throttling metadata
- Reference data that changes infrequently (zip codes, fee schedules, department lists)
Setting Up the Infrastructure
Let’s start with a Bicep template that deploys the PostgreSQL Flexible Server with settings optimized for a caching workload.
Bicep Deployment Template
| |
Deploy with the Azure CLI:
| |
Azure Government note: Azure Database for PostgreSQL Flexible Server is available in US Gov Virginia, US Gov Arizona, and US Gov Texas. Zone-redundant HA is supported in US Gov Virginia. When deploying to Azure Government, the server FQDN uses
postgres.database.usgovcloudapi.netinstead ofpostgres.database.azure.com. See Azure Government database services for endpoint details.
Configuring the .NET Application
First, install the NuGet package:
| |
Application Settings
Configure your appsettings.json with connection pooling parameters optimized for a caching workload:
| |
Note the connection string points to port 6432, which routes through the built-in PgBouncer connection pooler. This is critical for caching workloads, where many short-lived connections would otherwise overwhelm PostgreSQL’s process-per-connection model. PgBouncer supports up to 10,000 concurrent client connections with minimal overhead. See PgBouncer in Azure Database for PostgreSQL for configuration details.
Service Registration
Register the distributed cache in Program.cs:
| |
The CreateIfNotExists option automatically creates the cache table on first use, which simplifies deployment. For production, you may prefer to create the table as part of your database migration pipeline for tighter schema control.
Implementing the Cache-Aside Pattern
Here is a complete service implementation using the cache-aside pattern, which is the most common caching strategy:
| |
This pattern ensures:
- Fast reads: Cached data is returned without hitting the primary database
- Staleness control: Sliding expiration keeps hot data fresh; absolute expiration prevents unbounded staleness
- Explicit invalidation: When a permit status changes, the cache entry is removed so the next read fetches fresh data
A Generic Cache Helper
To avoid repeating cache-aside boilerplate across services, consider a reusable extension method:
| |
Usage becomes a single call:
| |
Performance Tuning
PgBouncer Configuration
For caching workloads, tune PgBouncer’s server parameters through the Azure Portal or CLI:
| |
Transaction pooling mode is ideal for caching because each cache operation is a short, independent query. This maximizes connection reuse.
Write-Ahead Logging Consideration
The UseWAL option in the Postgres cache configuration controls whether the cache table uses PostgreSQL’s Write-Ahead Logging. Setting UseWAL = false (the default) creates the cache table as UNLOGGED, which significantly improves write performance because PostgreSQL skips writing changes to the WAL. The trade-off is that unlogged table data is lost after a server crash or unclean shutdown. For caching, this is typically acceptable since cache data is ephemeral by design.
If your cache stores data that is expensive to recompute (complex report aggregations that take minutes), consider setting UseWAL = true for durability at the cost of some write performance.
Cache Key Design
Effective cache key design prevents collisions and enables bulk invalidation:
| |
When to Choose PostgreSQL vs. Redis for Caching
| Factor | PostgreSQL Cache | Azure Cache for Redis |
|---|---|---|
| Additional service to manage | No | Yes |
| Sub-millisecond latency | No (single-digit ms typical) | Yes |
| Azure Government availability | Yes (3 regions) | Yes (2 regions) |
| Connection pooling built-in | Yes (PgBouncer) | N/A |
| Data structure support | Key-value only | Lists, sets, sorted sets, streams |
| Cost for small workloads | Lower (shared with existing DB) | Higher (dedicated instance) |
| FedRAMP High authorized | Yes | Yes |
Choose PostgreSQL caching when: you already run PostgreSQL, your caching needs are straightforward key-value, and you want to minimize infrastructure complexity. Choose Redis when: you need sub-millisecond latency, advanced data structures, or pub/sub capabilities.
Why This Matters for Government
Government agencies face a unique combination of constraints: strict compliance requirements (FedRAMP, StateRAMP), limited staffing for infrastructure management, and increasing demand from residents for responsive digital services. The PostgreSQL distributed caching pattern addresses all three.
Reduced attack surface: Every additional service in your architecture is another component to patch, monitor, and secure. By using your existing PostgreSQL instance for caching, you eliminate a separate caching service from your Authority to Operate (ATO) boundary documentation.
Lower total cost of ownership: Government IT budgets are scrutinized carefully. Consolidating caching into your existing database eliminates the cost of a dedicated Redis instance and reduces operational overhead for teams that are already stretched thin.
Improved resident experience: Caching frequently accessed data - fee schedules, permit statuses, eligibility lookup results - means faster page loads for residents interacting with government portals. A reduction from 500ms to 50ms response time can meaningfully improve accessibility, especially for users on slower mobile connections.
Azure Government compatibility: Azure Database for PostgreSQL Flexible Server is available in US Gov Virginia, US Gov Arizona, and US Gov Texas, with zone-redundant high availability in US Gov Virginia. The Microsoft.Extensions.Caching.Postgres package works identically in both Azure Commercial and Azure Government. Just update your connection string endpoint from postgres.database.azure.com to postgres.database.usgovcloudapi.net.
Compliance alignment: PostgreSQL Flexible Server supports Microsoft Entra authentication, eliminating the need to store database passwords. Combined with Azure Private Link and virtual network integration, this meets the zero-trust networking requirements that many state and local agencies are adopting.
