Unit 5.5: In-Memory Database (Cache Tier)
Introduction
Unit 5.3 established a hard platform boundary: managed PostgreSQL and MariaDB on IONOS have no read replicas, no managed replication to a second cluster, and no cross-region replication. The synchronous and asynchronous replicas inside a cluster exist for failover, not for serving read traffic. That leaves an obvious question for any read-heavy workload: where does read scaling come from? The answer is this unit. The In-Memory DB cache tier absorbs the read load that a relational read replica would have carried, and in doing so it closes the no-read-replica gap with a native pattern rather than a missing feature.
The same tier does a second job that matters just as much. Unit 4.3 showed that VM Auto Scaling can only add and remove stateless replicas safely. Any session, lock, or transient state that lives in a compute node's memory is lost the moment that node is scaled in. Externalising that state into a shared in-memory tier is what makes the application layer genuinely stateless, and therefore what makes auto-scaling safe. This unit ends by building the cluster in the Data Center Designer and connecting it on the private network in front of the relational tier.
1. The Read-Scaling and State-Externalisation Tier
In-Memory DB is offered through IONOS DBaaS as a managed, Redis OSS-compatible key-value store, pinned to the legacy Redis OSS stable version 7.2. It is a data structure server, not only a flat cache: it stores strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, geospatial indexes, and streams. That breadth lets one tier serve two architectural roles at once.
Why it substitutes for read replicas. A relational read replica scales reads by duplicating the dataset onto another node that answers SELECTs; IONOS DBaaS does not offer that. Instead, the application reads through a cache: the first request for a value misses and hits PostgreSQL or MariaDB, the result is written into In-Memory DB, and every subsequent request is served from memory at sub-millisecond latency without touching the database. Leveraging the cache minimises database queries, significantly reducing traffic and the resources needed, enabling a rapid and cost-effective scaling layer. The relational primary stays small because it only ever sees misses and writes, while read throughput scales with cache memory rather than database cores. This is the design choice flagged in Unit 2.4 as cache-based scale-out: you buy RAM in the cache tier instead of vertically scaling the database.
Why it is the auto-scaling precondition. When the application tier holds session state in local process memory, every scale-in event destroys the sessions pinned to the removed node, and every scale-out starts a cold node other requests cannot see. Moving session and transient state into the shared In-Memory DB tier means any compute replica can serve any request, which is exactly the stateless precondition Unit 4.3 requires before a metric-driven auto-scaling group can add or remove Dedicated Core nodes without breaking users. The cache tier is therefore the read half of the no-read-replica pattern (5.3) and the state-externalisation half of safe auto-scaling (4.3).
What scaling the cache does and does not do. An In-Memory DB cluster scales two ways, and the distinction is easy to get wrong. Vertical scaling changes the cores and RAM per node and is how you grow throughput; nodes are modified after being turned off, so multi-node clusters keep disruption minimal by changing secondaries first, then the primary. Horizontal scaling changes the number of nodes, but the documentation is explicit that this provides high availability only and will not increase performance: the primary accepts all reads and writes and the secondaries are standby capacity for automatic failover, not additional read endpoints. So the cache scales reads through memory size, not by spreading reads across replicas, mirroring the relational tier's own no-read-replica reality one layer up.
1.1 FinCorp: closing the read-scaling gap
FinCorp's customer-portal read path was the open problem left by Unit 5.3. Account-summary and transaction-history views are read many times more often than they are written, and the regulated PostgreSQL cluster on the private data tier cannot be scaled by adding read replicas. FinCorp places an In-Memory DB cluster on the same private LAN, in front of the PostgreSQL cluster, and routes portal reads through it. Hot account summaries are served from memory; the database is touched only on a cache miss or a write. The same cluster holds portal session state, so the public-facing stateless web tier can be put under the Dedicated Core auto-scaling group designed in Unit 4.3 without losing sessions when nodes scale in. One managed tier resolves both constraints, and it never leaves the private network.
2. Cache-Aside vs Write-Through
The cache only helps if the application integrates it with a deliberate read-and-write strategy. Two patterns dominate, and the choice is an application-architecture decision that the platform does not make for you.
The following table contrasts the two integration patterns:
| Pattern | How reads work | How writes work | Best for | Main risk |
|---|---|---|---|---|
| Cache-aside (lazy loading) | App checks cache; on miss, reads database, then populates cache | App writes to the database and invalidates or updates the cache key | Read-heavy workloads where not all data is hot | Stale entries if invalidation is missed; first read of any key always misses |
| Write-through | App reads from cache (assumed populated) | App writes to the cache and the database synchronously as one logical step | Workloads needing freshly cached data immediately after a write | Higher write latency; cache holds data that may never be read |
Cache-aside is the default for a read-scaling tier because it only caches data something actually asked for, keeping cache memory focused on the working set. Its cost is the cold-miss on first access and the discipline of invalidating or refreshing a key on every write so the cache does not serve a stale value after the database changes. For FinCorp's portal, where a small set of accounts is read constantly and most are read rarely, cache-aside keeps the hot working set resident and leaves the long tail to the database.
Write-through keeps the cache authoritative immediately after a write by writing both stores together, which suits data read very soon after it is written. It pays for that freshness with write latency, because each write now waits on both stores, and it can fill the cache with entries never subsequently read.
A practical rule: pick a Time To Live on cached keys that matches how stale a value may be, and treat eviction as a backstop, not the primary expiry mechanism. Because the cache substitutes for read scaling and is not a system of record, the application must always be able to reconstruct any value from the relational tier on a miss. The persistence options below harden the cache against restarts, but the relational cluster remains the source of truth.
3. Placement, Sizing, and Eviction
3.1 Private-network placement
In-Memory DB attaches to exactly one network connection: a single private LAN connection per cluster, defined by a data center, a LAN id, and an IP in that LAN's subnet. There is no public endpoint and no IP address management for the cluster, so you choose an IP inside your subnet (or use the subnet IP the platform assigns under DHCP) and the cluster becomes reachable on that address after provisioning. This is the same private-data-tier placement used for the relational and document databases in this module: the cache sits on the private LAN in front of the relational cluster, reachable only from the application tier and the database tier, never from the internet. Clients connect over TLS only if the cluster has been explicitly configured to use it (TLS is not on by default), with the server certificate issued by Let's Encrypt when enabled, so the architect must decide to enable TLS and then have the client connect with the TLS option and the corresponding CA certificate.
The following IP ranges are reserved by the platform and cannot be used for In-Memory DB connections:
| Reserved range |
|---|
| 10.208.0.0/12 |
| 10.233.0.0/18 |
| 192.168.230.0/24 |
| 10.233.64.0/18 |
3.2 Sizing from RAM and persistence
You do not provision the cache's disk directly. You choose the number of CPU cores and the RAM per node, and the platform derives the provisioned Block Storage from the configured RAM and the chosen persistence mode, with a minimum of 10 GB per node applied for every mode. The relationship is fixed:
| Data persistence | Provisioned storage |
|---|---|
| None | 1 x RAM |
| RDB | 2 x RAM |
| AOF | 4 x RAM |
| RDB and AOF | 8 x RAM |
Persistence mode is the sizing lever that matters most. The default is None, meaning the dataset lives only in memory and is lost on a restart; for a pure read-through cache that can always rebuild from the relational tier, None is often the correct and cheapest choice. RDB writes periodic point-in-time snapshots; AOF logs every write operation and can reconstruct the dataset on restart; RDB_AOF combines both for the most durable, and most storage-hungry, configuration. The trade-off is direct: more durability multiplies the provisioned storage against RAM, so a 32 GB node sized at the maximum costs 32 GB of disk under None but 256 GB under RDB and AOF. Size the RAM to your working set plus headroom for eviction, then pick the lowest persistence mode the workload's recovery requirements allow.
The hard ceilings, against your contract quota, are a maximum of 16 CPU cores, 32 GB RAM, and 2 TB storage per cluster, up to 5 nodes per cluster. The underlying Valkey engine defaults to a maximum of 10,000 client connections (maxclients), which is a technology default rather than a published IONOS limit.
3.3 Eviction
When a cache node reaches its memory limit on incoming data, the eviction policy decides what is removed. The default policy is allkeys-lru, which evicts the least-recently-used key across all keys, the right behaviour for a general read-through cache where any key is a candidate for removal. The full set of supported policies is noeviction, allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru, volatile-lfu, volatile-random, and volatile-ttl. The volatile-* policies only evict keys that carry an expiry, which is appropriate when some keys must never be evicted; noeviction rejects new writes once memory is full, which turns a cache into a hard failure point and is rarely correct for a scaling tier. For a cache-aside read tier, keep the default allkeys-lru (or allkeys-lfu if access frequency is a better signal than recency) so the cache always makes room for the current working set.
DCD Implementation Walkthrough
This walkthrough provisions a single In-Memory DB cluster and places it on FinCorp's private data tier so the application can use it as a read-through cache in front of the PostgreSQL cluster from Unit 5.3. The prerequisite is an existing VDC with at least one server on a private LAN: the cluster must join an existing private LAN, and you must have an IP in that LAN's subnet to assign it. Reuse the FinCorp VDC and private data-tier LAN built in Module 3.
Build goal: Provision the cache and wire it as a read-through layer.
Steps (in the Data Center Designer):
- In the DCD, go to Menu > Databases > In-Memory DB. The In-Memory DB cluster overview opens and shows the resources allotted to your contract.
- Click Create cluster.
- Define cluster properties. Enter a cluster Name; leave Version at the default 7.2. Set Instances to the number of nodes: choose 1 for a single-node cache or up to 5 for high availability. Remember that extra nodes provide failover, not read throughput. If you set more than one node, a Replication Type option appears; it is Asynchronous by default for In-Memory DB.
- Select the required number of resources. Use the sliders to set the Number of CPUs and the RAM Size per instance. RAM is the real sizing decision: size it to the working set, because the persistence multiplier and the per-node disk derive from it. Stay within the 16-core and 32 GB per-cluster ceilings.
- Connect the cluster with a VDC. Select the data center, the private LAN, and an IP address (with CIDR) inside that LAN's subnet. Only one connection is allowed per cluster, and it must be private. Choose an address that does not collide with existing hosts and is not inside a reserved range.
- Schedule the cluster maintenance. Set the day of week and the start time for the weekly maintenance window. Pick a genuine low-traffic window rather than leaving it unscheduled, because version upgrades and restarts happen here and may briefly interrupt connections.
- Define users. Enter a Username and Password for the cluster. These credentials can only be set at creation and cannot be changed afterward, so record them in your secret store immediately.
- Review the estimated cost shown for the configuration, then click Save. The cluster STATE shows Busy during creation and Available when ready; the platform does not notify you, so poll until the state is Available.
Once the cluster is Available, point the application's cache client at the assigned private IP on the default port 6379 (over TLS if you enabled it at instance configuration), and implement the cache-aside read path from Section 2: check the cache, fall back to PostgreSQL on a miss, and populate the cache on the way back.
Common mistakes:
- Treating extra nodes as read scaling. Adding nodes provides high availability only and does not increase performance; scale reads with more RAM and a vertical resize, not more replicas.
- Forgetting that credentials are set only at creation. The username and password cannot be updated later, so store them at creation time; recovering from a lost password means rebuilding the cluster.
- Leaving persistence at the wrong tier for the cost. None loses all data on restart but provisions the least disk; RDB and AOF together provisions 8x the RAM as disk. For a pure read-through cache that rebuilds from the database, None is usually correct.
- Choosing
noevictionfor a scaling cache. When memory fills,noevictionrejects new writes and turns the cache into a failure point; keep theallkeys-lrudefault unless a specific key must never be evicted. - Assigning an IP in a reserved range, or expecting a second connection. A cluster takes exactly one private LAN connection, and the platform-reserved ranges (for example 10.233.0.0/18) cannot be used.
- Not scheduling a real maintenance window. Upgrades and restarts run in the weekly window and can briefly drop connections, so place it deliberately in a quiet period and have clients reconnect.
A short client connection illustrates the TLS-and-private-endpoint constraint that the prose establishes:
redis-cli -h 192.168.1.100 -p 6379 --tls --cacert ca.crt -a "$PASSWORD" ping
The host is the private LAN IP assigned in step 5, the port is the default 6379, and the --tls --cacert pair is only needed if TLS was enabled at instance configuration (the managed endpoint terminates TLS with a Let's Encrypt certificate when enabled, but TLS is not on by default). There is no public address to connect to, which is the point: the cache is reachable only from inside the VDC.
Summary
The In-Memory DB cache tier is the single component that resolves two platform boundaries at once. It is the native substitute for the read replicas that managed PostgreSQL and MariaDB do not offer, absorbing read load through memory so the relational primary only sees writes and cache misses, and it is the shared state store that makes the application tier truly stateless and therefore safe to put under VM Auto Scaling. Built on the private data tier with a cache-aside read path, sized from RAM and persistence mode, and left on its sensible eviction default, it lets FinCorp scale reads and scale compute without ever introducing a database read replica or a public endpoint.
Key Points:
- There are no relational read replicas on IONOS; the In-Memory DB cache is how you scale reads, by serving the hot working set from memory and touching the database only on misses and writes.
- The cache is also the state-externalisation tier that makes the stateless precondition for auto-scaling (Unit 4.3) real.
- Adding nodes to the cluster provides high availability, not read throughput; grow capacity by adding RAM and resizing vertically.
- Provisioned disk is derived from RAM times the persistence multiplier (None 1x, RDB 2x, AOF 4x, RDB+AOF 8x), with a 10 GB-per-node floor; choose the lowest persistence the recovery requirement allows.
- The cluster takes exactly one private LAN connection, with TLS available if you enable it at instance configuration, has no public endpoint, and its credentials are fixed at creation; the default
allkeys-lrueviction is correct for a read-through cache.
Important Terminology:
- Cache-aside: A read-through pattern where the application reads the cache, falls back to the database on a miss, and populates the cache with the result; writes go to the database and invalidate or update the cached key.
- Write-through: A pattern where the application writes to the cache and the database together, keeping the cache authoritative immediately after a write at the cost of write latency.
- Persistence mode: The In-Memory DB setting (None, RDB, AOF, or RDB_AOF) that controls whether data survives a restart and, through a fixed multiplier on RAM, how much Block Storage the cluster provisions.
- Eviction policy: The rule (default
allkeys-lru) that decides which keys are removed when a node reaches its memory limit.
Further Reading
- Unit 5.3: Relational Databases, for the no-read-replica boundary this tier closes
- Unit 4.3: Elasticity and VM Auto Scaling, for the stateless precondition this tier satisfies
- Unit 2.4: Cost Architecture and FinOps, for cache-based scale-out economics versus vertical database scaling