This post assumes familiarity with Valkey ACL.
Choosing a multi-tenancy isolation model for Amazon ElastiCache for Valkey comes up in nearly every SaaS architecture review, and the answer is almost never the same twice.
SaaS is not one-size-fits-all. A healthcare application with 20 tenants and strict regulatory boundaries operates under fundamentally different constraints than an analytics service with 20,000 tenants on shared infrastructure. They differ in tenant count, compliance posture, data sensitivity, traffic patterns, and cost tolerance. The isolation question is identical. The correct architecture is not.
To arrive at the right model, consider both dimensions together. Tenant count determines what is operationally feasible: a hundred tenants on dedicated clusters is viable. Ten thousand is not. The scope of failure and compliance posture determine how much isolation each tenant needs. Ten thousand tenants on a shared cluster with no cryptographic boundary might be unacceptable if a single cross-tenant inadvertent disclosure triggers a regulatory event. Tenant count constrains the design space. The scope of failure and compliance posture selects the point within it.
In this post, we walk you through the multi-tenancy options available for Amazon ElastiCache for Valkey, organized by the isolation guarantees they provide, from full physical separation down to application-enforced logical boundaries. We then tackle the security question that always follows: what do you do when your tenants require per-tenant encryption on a service that doesn’t natively support it?
How to think about isolation
The SaaS Lens for the Well-Architected Framework defines several isolation strategies. At the level of a specific resource such as the cache layer, the choice reduces to two: silo, where each tenant runs on dedicated infrastructure and the boundary is enforced by the resource itself, or pool, where tenants share infrastructure and the boundary is enforced logically within it. This post explores how each of those two strategies can be implemented on Amazon ElastiCache, from the strongest silo options down to the lightest pool configurations.
Choosing between them comes down to three dimensions. The first is the scope of potential failure, and it has two faces. A tenant’s traffic spike or runaway operation can saturate CPU or memory, degrading performance for all other tenants on the same cluster. At the same time, a bug or misconfiguration that causes a cross-tenant read has very different consequences depending on whether the disclosed data is plaintext or ciphertext. A shared cluster without per-tenant encryption means a data-access failure exposes usable data. A shared cluster with per-tenant encryption means the same failure exposes only encrypted bytes that the reader cannot decrypt.
The second dimension is where the trust boundary sits. In a server-enforced model, the engine rejects unauthorized access regardless of what the application code does. In an application-enforced model, isolation depends on the code constructing the correct key prefix, selecting the correct database, and never making a mistake.
The third dimension is compliance posture. Some regulatory frameworks (SOC 2 Type II, HIPAA, FedRAMP) mandate demonstrable isolation between tenants. An auditor does not treat “our code handles it correctly” the same as “the infrastructure rejects unauthorized access.” Cryptographic proof that leaked data is undecryptable carries more weight still. The strength of evidence the compliance framework demands narrows the set of viable architectures.
Full physical isolation: Dedicated cache clusters
The strongest isolation model gives each tenant their own ElastiCache cluster with dedicated compute, dedicated memory, and a dedicated endpoint. No shared resources. The infrastructure enforces the boundary. A failure, traffic spike, or data corruption in one tenant’s cache does not impact any other tenant.
This model is clear in concept but demanding in practice. Each new tenant requires creating a cluster, configuring security groups, and setting up parameter groups. Once these are in place, the endpoint must be registered in a routing layer. The routing layer itself is infrastructure the SaaS provider builds and maintains. It maps tenant to cluster endpoint and is typically backed by a configuration store such as Amazon DynamoDB or a parameter service. It must handle onboarding, endpoint rotation during failover, and decommissioning when a tenant churns. None of this comes for free.
With provisioned clusters, each tenant’s cache is sized for peak load. Tenants with variable traffic sit on idle capacity most of the time, and that waste multiplies across the entire tenant base. There is also the ongoing burden of right-sizing reviews: as tenant traffic patterns shift, clusters need resizing to avoid both overspend and performance degradation. Maintenance windows, patching, version upgrades, monitoring, and alerting all scale linearly with the number of clusters. A solution with 200 tenants means 200 clusters to monitor, 200 sets of Amazon CloudWatch alarms, and 200 maintenance windows to coordinate.
Then there are the service quotas. ElastiCache allows 300 nodes per Region for provisioned clusters by default. If the smallest high availability (HA) configuration is two nodes (primary + replica), that ceiling supports roughly 150 tenants before a quota increase is needed. Virtual private cloud (VPC) security groups (500 per VPC by default) can become another constraint if each tenant cluster has its own. These are soft limits, adjustable through Service Quotas, but each increase requires a support request and lead time. At hundreds of tenants, quota planning becomes part of the capacity planning cycle rather than a one-time setup step. Limitations also can be solved by adding more AWS accounts, but it makes the solution even more complex.
Reducing the operational burden with serverless
Amazon ElastiCache Serverless removes several of these challenges, though not all of them.
With serverless, there is no capacity planning. Each cluster scales automatically with load, eliminating overprovisioning and the need for right-sizing reviews. There are no maintenance windows to schedule and no node types to choose. Onboarding a new tenant becomes a more streamlined operation: create a serverless cache and register the endpoint in the routing layer.
The cost model shifts from fixed to consumption-based. Instead of a fixed hourly rate per node regardless of utilization, the SaaS provider pays per ElastiCache Processing Unit (ECPU) consumed plus storage. For a tenant with minimal data and near-zero request volume, the cost floor is approximately $6/month (the 100 MB minimum storage charge in US East (N. Virginia) Region). Compare this to the smallest viable provisioned HA configuration (cache.t4g.micro with primary + replica) at approximately $19–23/month, paid whether the tenant uses it or not. For current pricing, see the Amazon ElastiCache pricing page.
Serverless does not solve everything. The default quota is 40 serverless caches per Region, lower than the 300-node provisioned limit, so the ceiling may be reached sooner (though both are adjustable). The routing layer is still required. Per-tenant monitoring is still required.
Despite these limitations, serverless is typically the better starting point for the dedicated cluster model. It delivers the same isolation guarantees with lower operational overhead and a lower cost floor. Provisioned clusters remain the right choice for tenants with steady-state workloads or specific performance requirements tied to a known instance type.
Figure 1: Full physical isolation with Amazon ElastiCache Serverless, where tenant endpoints load from Amazon DynamoDB at startup
Server-enforced logical isolation: ACL/RBAC on a shared cluster
When dedicated clusters are more separation than a tenant needs, the next model down places all tenants on a single shared cluster and lets the engine enforce the boundary between them.
ElastiCache supports Role-Based Access Control (RBAC) using Valkey/Redis ACL syntax, where each tenant gets a dedicated user whose permissions are scoped to their own keyspace, such as ~tenant_42:*. The application authenticates the tenant-specific user, and the engine rejects any operation that reaches outside that user’s permitted key pattern. The practical consequence is worth stating plainly: a bug in the application code that constructs the wrong key prefix returns an authorization error rather than another tenant’s data. The boundary that protects tenants from each other lives on the server, not in the correctness of the code.
That guarantee is narrower than it first appears, because it covers data access and nothing else. All tenants on the cluster share the same compute and the same memory, so the scope of failure splits in two. A cross-tenant read or write is blocked server-side and stays contained to a single tenant. A cluster outage, memory exhaustion, or CPU saturation is not contained at all and reaches everyone. The trust boundary is server-enforced for data and entirely absent for resources. This is the right trade when the requirement is that tenants cannot read each other’s data. It is the wrong trade when tenants must also be insulated from each other’s load. This model isolates data but not compute and memory.
The shared substrate is where the trade-offs concentrate. There is no memory or CPU isolation, so one noisy tenant degrades performance for all tenants co-located with it, and there is no per-tenant scaling, because the cluster scales as a single unit rather than tenant by tenant. The key-prefix discipline is also the operator’s to maintain. ACLs restrict access by pattern, but they do not prefix keys automatically, so the application stays responsible for constructing each key correctly even though the engine is what enforces the restriction.
When multiple tenants share a cluster, you need to have control over key distribution between shards. In Valkey, you can force different keys to map to the same slot, and therefore the same shard, by using hash tags. For example, the keys user:{tenantA}:profile and user:{tenantA}:settings land on the same slot because their hash tags ({tenantA}) are identical. This is useful when performing MGET/MSET operations or running Lua scripts, where all keys must reside on a single shard.
The drawback is hot slots. Different tenants typically have varying key counts, memory footprints, and traffic characteristics. Forcing all tenant’s keys into one slot often causes cluster imbalance: some shards become heavy in memory and CPU utilization while others remain underutilized. Because all nodes in a cluster are identically sized, you end up scaling based on the heaviest shard.
The common best practice is to avoid hash tags for tenant scoping and instead let keys distribute evenly across all slots. Reserve hash tags for the specific operations that genuinely require slot co-location (multi-key commands, Lua scripts) and scope them narrowly rather than applying them to an entire tenant’s keyspace.
Scale is the last consideration, and it sets a firm ceiling. Access control is organized through user groups, and a user group holds roughly 100 users, which puts a practical limit near 100 tenants on a single cluster when running one user per tenant. That limit is a soft quota rather than a hard wall. An increase can be requested through a support case, the same way the node quota for dedicated clusters can be raised. It does not stretch arbitrarily, though, and planning around it belongs in the design rather than as an afterthought. Within that ceiling, ACL/RBAC on a shared cluster gives the lowest cost per tenant of any server-enforced option. That is exactly why it fits solutions with a modest tenant count that need a real data boundary without paying for isolated infrastructure.
Server-enforced logical isolation with Amazon ElastiCache for Valkey multi-database
When key-prefix isolation is too fragile, but dedicated clusters are too expensive, Valkey 9.1 on ElastiCache offers a middle ground: numbered databases plus database-scoped ACLs. Each tenant gets its own logical database (0–N), and each tenant user is restricted to a specific set of databases. The engine enforces the database boundary, so a misconfigured client cannot simply SELECT into another tenant’s namespace.
Valkey 9.0 brought numbered databases to cluster mode, turning them from a single-instance convenience into a scalable namespacing building block. Valkey still shards keys by slot across the cluster. The database index is purely logical. That means clean namespace separation is achievable without changing the sharding topology or key distribution. You assign each tenant a database index and reach it with the SELECT command, so there are no key prefixes to construct and no prefix-parsing bugs to guard against. The engine enforces the namespace boundary rather than relying on key shape.
Valkey 9.1 adds the missing piece: per-user database scoping in ACLs (for example, db=0,1). Now each tenant or service can have its own credentials, and the server rejects attempts to access databases outside that set. The Valkey 9.1 for Amazon ElastiCache announcement highlights this combination as a way to strengthen isolation and governance for shared-cluster workloads.
A minimal example:
After authentication, that user can operate in database 42:
But cannot switch to another database:
This model is a good fit when the primary goal is logical isolation and operational consolidation, not hard resource isolation. It works well for internal services, lower-risk tenants, and environments where the cost of separate clusters is hard to justify. It does not prevent noisy-neighbor effects: a busy tenant can still consume CPU, memory, and network on the same cluster.
Scope of failure: All tenants on the cluster share compute and memory. A cluster outage, memory exhaustion, or CPU saturation hits everyone. Data access violations are contained to a single tenant if database-scoped ACLs are used correctly. Without them, any authenticated client can reach any database.
Trust boundary: Server-enforced for data access when numbered databases are combined with database-scoped ACLs. The engine itself rejects operations outside a user’s permitted databases. Resource isolation (memory, CPU) is still shared.
This isolation method fits best when the following conditions hold:
- Cleaner namespace separation than key prefixes is needed, without per-tenant clusters.
- The compliance requirement is “tenants cannot access each other’s data” rather than “tenants cannot impact each other’s performance.”
- A shared-compute model is acceptable for the workload.
- Consolidating many tenants onto fewer clusters to reduce operational overhead is a goal.
Trade-offs:
- Shared resources between tenants (CPU, memory, and networking). One noisy tenant can degrade performance for everyone on the cluster.
- Compared to the serverless cluster per tenant approach there’s no per-tenant scaling: the cluster scales as a unit.
- ElastiCache for Valkey supports up to 10,000 databases per cluster (the default is 16, and Valkey OSS imposes no hard limit). However, each additional database adds metadata and CPU overhead. We recommend staying in the low thousands and load-testing against your production workload before scaling further.
A tiered strategy
Most SaaS applications run several of these models at once. The tier a tenant lands in follows from what its contract, compliance posture, and risk tolerance require, not from the system’s total tenant count.
The real difficulty in running several tiers is not the isolation itself but keeping the application from needing to know which tier a tenant is in. When each tier is addressed differently, the layer that resolves the tenant stops being a router and becomes a query translator that rewrites every operation, which does not scale across the full surface of cache commands. The way out is to keep the access pattern identical across tiers and let only the connection target and an optional encryption step vary. A tenant-aware data-access layer resolves each tenant to an endpoint and a database index, then applies them transparently, so application code issues the same call regardless of tier.
The concern this raises is performance: an in-memory database exists for low latency, so a resolution step in front of every call looks self-defeating. In practice it is not. The tenant-to-target mapping changes only when a tenant is provisioned or migrated, so it lives in an in-process map that the resolver reads in nanoseconds, against a cache round trip measured in hundreds of microseconds. The lookup is orders of magnitude cheaper than the call it precedes. This is not new work: any multi-tenant cache already has to scope each request to a tenant, whether by key prefix or database selection. The one rule that keeps it fast is to cache the mapping in process rather than resolving it against a remote store on the hot path.
The namespace mechanism has to preserve that uniformity without giving up horizontal scale, and Valkey numbered databases provide it. Each tenant gets a numbered database, and because the database index is orthogonal to slot assignment, a tenant’s keys scatter across every shard exactly as they would in the default database.
The security aspect: Per-tenant encryption
The isolation discussion is almost always followed by a question about whether ElastiCache can encrypt each tenant’s data with a unique key. Answering it requires being precise about what ElastiCache encryption protects. Data in memory is plaintext. The engine does not encrypt values while they are resident, so there is nothing to key per tenant at that layer. Encryption at rest protects data only when it touches disk during synchronization, backups, swap, exports to Amazon Simple Storage Service (Amazon S3), and multi-Availability Zone (AZ) durability logs, and encryption in transit protects data between clients and nodes. Both operate at the cluster level with a single key. Neither offers a per-tenant key.
A tenant that cannot tolerate plaintext in memory must have the application encrypt values before they reach the cache. A tenant that is comfortable with plaintext in memory but needs a dedicated key for the disk-touching operations must either run on a dedicated cluster with its own encryption-at-rest key, or accept client-side encryption as the alternative. The dedicated-cluster path was covered earlier. The client-side path is the focus here.
The recommended approach is the AWS Key Management Service (AWS KMS) hierarchical keyring from the AWS Encryption SDK. A single KMS key protects many tenant-specific branch keys held in an Amazon DynamoDB-backed key store. The application gets per-tenant keys without per-tenant infrastructure, while KMS handles rotation, access policies, and an audit trail through AWS CloudTrail. Branch keys are cached in the application with a configurable time-to-live (TTL) that trades KMS cost against how long an exposed branch key stays usable. Because branch keys are cached in the application, steady-state reads stay at cache speed, and only a cold or expired key triggers a call to KMS and DynamoDB. A cross-tenant inadvertent disclosure on a shared cluster then exposes ciphertext rather than plaintext, which makes this a strong compensating control for the application-enforced isolation models.
Client-side encryption does complicate lookups. Randomized encryption produces different ciphertext each time, so a value such as an email address cannot serve as a searchable cache key. Some data types may even become useless once encrypted: sets can no longer detect duplicates or check membership, string counters lose atomic INCR, and stream entry IDs must stay in plaintext for the server to order and read them.
The AWS Database Encryption SDK addresses this with beacons: a truncated Hash-Based Message Authentication Code (HMAC) computed over the plaintext and stored alongside the still-randomized ciphertext. Searchable encryption requires the hierarchical keyring, which generates a distinct beacon key per branch key, so each tenant’s beacons are isolated. The beacon feature itself is implemented only for Amazon DynamoDB, where the SDK maintains the beacon indexes and answers beacon queries. There is no equivalent implementation for ElastiCache.
The pattern still applies to a cache, but the application has to build it. The steps are:
- Obtain the per-tenant beacon key from the hierarchical keyring through the AWS Cryptographic Material Providers Library.
- Compute the HMAC in application code.
- Use the result as the cache key.
These deterministic lookups leak equality, so they suit index values but not the sensitive payloads, which stay randomized.
Key rotation is straightforward for a cache. A rotated branch key encrypts new writes while the old version continues to decrypt existing data, consistent with the process-only state described in NIST SP 800-57 Part 1 Rev 5. Because cached entries expire on their own TTL, old data simply ages out and no batch re-encryption job is needed. Re-encryption is warranted only on confirmed inadvertent access of a specific branch key, and even then the cleanest response is usually to flush the affected tenant’s keyspace and let it repopulate from the source of truth.
Choosing the right model
Start with the scope of failure the business can tolerate, not the number of tenants.
If a single-tenant failure must never affect anyone else, and the compliance framework agrees, use dedicated serverless caches for those tenants. Full stop.
If server-enforced access boundaries are needed but shared compute is tolerable (the most common mid-market SaaS posture): ACL/RBAC or Valkey 9.1 database ACLs.
If the goal is optimizing for cost at scale and the threat model accepts application-enforced boundaries: use a cluster pool with client-side encryption as a compensating control. The encryption means that even if isolation fails at the application layer, the disclosed data is cryptographically useless without the tenant’s branch key.
The right answer is almost always a mix. Premium tenants get dedicated infrastructure. Standard tenants get shared infrastructure with server-enforced or cryptographic boundaries. The cache layer does not need to be the hardest part of a multi-tenant architecture, but it does need to match the isolation guarantees promised to tenants.
To get started, review the Amazon ElastiCache documentation for details on configuring RBAC, serverless caches, and encryption options. For a deeper dive into SaaS isolation patterns across the full stack, see the SaaS Tenant Isolation Strategies whitepaper.






