On fixed dedicated hardware, the winning move is to size each role by the resource it actually saturates, not by reaching for a bigger tier.
On elastic public cloud, the reflex when a stateful service struggles is to move it to a larger instance type. The abstraction encourages it: pick a family, slide the size up, pay more per hour. On fixed dedicated hardware that reflex is wrong, because a dedicated server is not an elastic slider. It is a specific budget of cores, memory channels, and PCIe lanes, and the right choice is the SKU whose bottleneck matches the bottleneck of the role you are placing on it.
That reframing is the whole thesis. A stateful platform is not one workload, it is a set of roles, and each role saturates a different resource first. A Kubernetes worker fleet is core and scheduling bound. A MongoDB replica set and a Redis tier are memory bound. A Kafka broker and a ClickHouse node are storage-throughput and PCIe-lane bound. A PostgreSQL primary is a hybrid: memory for its buffer pool, low-latency NVMe for its write-ahead log. And a coordination tier, an etcd cluster or a Kafka KRaft controller quorum, is bound by fsync latency and blast radius, not by capacity at all. Match each role to the SKU whose dominant resource matches the role’s dominant demand, and you get better useful work per dollar and a smaller blast radius than you get by homogenizing every role onto one large tier. The second half of the argument is that on dedicated hardware a node is a whole server, so per-node capacity sets how many nodes a quorum needs and how much a single node failure costs. That makes the generation choice a topology decision, not just a price one.
Key Takeaways
- Size by role, not by tier. Each stateful role saturates a different resource first, so the SKU whose bottleneck matches the role beats the biggest available tier on cost-efficiency.
- Core-bound roles want the Large v4. A Kubernetes worker fleet runs many pods and threads; the Large v4’s 32 cores and 64 threads with 512GB DDR5 feed scheduling and compute density without paying for memory the role will not use.
- Memory-bound roles want the XL. A MongoDB replica set or a large Redis working set is governed by RAM and memory bandwidth; the 1TB XL v4 and XL v5 (the v5 on DDR5-6400 versus the v4 at DDR5-4800) keep the working set resident, and the XXL v4 doubles base RAM to 2TB on the same 64 cores when a single working set outgrows the XL.
- Throughput-bound roles want NVMe headroom. Kafka log append and ClickHouse columnar scans are drive-bound; the Micron 7500 MAX 6.4TB sustains about 5,900 MB/s sequential write and 1.1 million random read IOPS, with a documented sub-millisecond floor at six-nines for 4KB random reads.
- Not every role wants a big node. The coordination tier (etcd, KRaft controllers, config servers) is bound by fsync latency and blast radius, not capacity; a Medium v4 (24 cores, 256GB DDR5-4400) right-sizes it and keeps each quorum member a small failure domain.
- The node is the failure domain. Because a node is a whole dedicated server, per-node core and RAM capacity sets quorum node count and single-node blast radius, so the v3-to-v4/v5 generation choice reshapes replica topology, not just the invoice.
Size by role, not by tier
The method is three steps.
- First, name the resource each role saturates first under its real load: cores, memory capacity or bandwidth, or storage throughput and lanes.
- Second, pick the SKU whose largest budget is that resource.
- Third, check the failure domain the resulting node count creates.
The rest of this piece works each role through those steps. It deliberately does not restate the specifications on the Large v4, Medium v4, XL v4, and XL v5 pages. It argues how to choose among them.
Core-bound roles: the Kubernetes worker fleet
A Kubernetes worker’s job is to pack and run many pods. Its pressure points are core count, thread count, and the scheduler’s ability to keep those threads busy, not raw capacity of any single resource. The Large v4 fits this shape: two Intel Xeon Gold 6526Y processors give 32 cores and 64 threads at a 2.8 GHz base and 3.9 GHz turbo, with 512GB of DDR5-5200. That is enough memory to give pods real headroom without paying for a terabyte the worker role will rarely fill, and enough cores to run a dense pod population per node.
Choosing the Large v4 here rather than a 1TB XL is the discipline the thesis asks for. The XL’s extra memory would sit largely idle under a worker fleet, so its cost buys capacity the role does not consume. Sizing the worker to cores keeps the money on the resource that actually gates throughput.
There is a generation nuance worth naming before reaching for the newer part. A base Large v5 does not populate every memory channel, so it reaches full DDR5-6400 bandwidth only after a memory upgrade. For a worker fleet running many memory-touching pods, the fully populated Large v4 gives predictable per-pod bandwidth at its base configuration, which is usually the cleaner core-for-dollar pick unless you are already buying the v5 memory fill for another reason.
The coordination tier: small nodes on purpose
Not every role wants a large node. A Kubernetes control plane, an etcd cluster, a Kafka KRaft controller quorum, or a set of MongoDB config servers coordinates state rather than storing much of it. These roles are governed by fsync latency and by how much a single member’s loss costs the quorum, not by core count or capacity. The Medium v4, with two Intel Xeon Silver 4510 processors giving 24 cores and 48 threads and 256GB of DDR5-4400, is deliberately the right size here: enough to run the coordinator comfortably, small enough that losing one member removes only a small share of the quorum. Pointing this role at a large SKU spends money on capacity a coordinator never uses and, worse, concentrates the quorum onto fewer, larger failure domains. The six-nines NVMe latency floor discussed below matters as much to an etcd fsync path as it does to a database commit.
Memory-bound roles: MongoDB and Redis
A MongoDB replica set and a Redis tier live and die by memory. MongoDB wants its working set and indexes resident so reads are served from RAM rather than disk, and Redis is memory by definition. For these roles the resource to maximize is capacity and memory bandwidth, which is exactly what the XL tier leads with: 64 cores and 1TB of DDR5 per node on both XL v4 and XL v5.
The generation choice sharpens here. The XL v5, built on the newer Intel Xeon 6530P, runs its memory at DDR5-6400 across eight channels per socket, versus DDR5-4800 on the XL v4’s 6530. For a bandwidth-sensitive datastore serving high concurrent read volume, that memory-subsystem uplift matters more than any core-count change, because the bottleneck was never cores. Placing a memory-bound datastore on a memory-led SKU, and preferring the generation with the faster memory subsystem, is the same rule applied twice.
There is a ceiling to this, and it is where the XXL v4 comes in. It carries the same 64 cores and the same DDR5-4800 as the XL v4, but doubles base memory to 2TB across 32 DIMM slots, with room to grow well beyond that, and it carries six NVMe bays rather than a pair. When a single memory-bound datastore’s working set genuinely exceeds a terabyte and cannot be sharded smaller, the XXL v4 keeps it resident on one node instead of forcing a shard split, and its drive count suits a datastore that is heavy on both memory and storage. The discipline is to reach for it only when the working set actually demands it: an XXL is the large-node end of the failure-domain trade discussed below, so one of them is a bigger blast radius than two XLs.
A PostgreSQL primary makes the same point from the hybrid case. Its read performance is governed by whether the working set stays resident in the buffer pool, which is a memory-capacity argument for the XL tier, while its commit latency is governed by how fast the write-ahead log can fsync, which is the NVMe-latency argument of the next section. The discipline is to size Postgres to its binding constraint: for most OLTP that is the buffer pool, so the memory-led XL is the right home, with the write path leaning on the same low-latency NVMe the throughput roles depend on. A relational primary is not a fourth resource, it is a reminder that a role can straddle two, and that you size to whichever one saturates first.
Throughput-bound roles: Kafka and ClickHouse
Kafka and ClickHouse stress the storage path. A Kafka broker is a sequential-write engine: it appends to the log and flushes segments, so sustained write bandwidth and predictable write latency set its ceiling. ClickHouse is a scan engine: analytical queries sweep large columnar segments, so random and sequential read throughput govern query time. Both are drive-bound and lane-bound before they are core-bound.
This is where the drive itself is the spec that matters. The Micron 7500 MAX 6.4TB, the standard NVMe in these builds, sustains roughly 7,000 MB/s sequential read and 5,900 MB/s sequential write, delivers about 1.1 million random read and 400,000 random write IOPS, and, per the Micron 7500 product specification, holds a sub-millisecond latency floor at the six-nines percentile for 4KB random reads up to queue depth 128. That six-nines floor is the number a broker or an analytical node cares about, because it bounds the tail of flush and scan latency rather than just the average. Each drive attaches over a dedicated PCIe Gen4 x4 link, so multiple drives run at full width rather than contending on a shared lane budget, and the servers carry multiple NVMe bays so throughput scales with spindle count. For a Kafka or ClickHouse role, the SKU decision is really a decision about how much of that NVMe headroom you provision.
This is where the Storage line earns its place. Beyond the base Large v4’s pair of NVMe bays, the Storage tier pairs several NVMe drives with bulk SATA capacity, which suits the way these engines actually retain data: a Kafka broker keeps recent segments on NVMe and tiers older ones to SATA, and a ClickHouse node holds hot partitions on NVMe while cold partitions rest on cheaper spinning capacity. A moderate broker is fine on the base Large v4; a log or analytical tier with real retention wants the NVMe density and the SATA tier the Storage line provides.
The failure domain is the node
The last step is the one elastic cloud lets people ignore. On dedicated hardware a node is a whole server, so per-node capacity decides how many nodes you need for a given total and how much one node failure removes from the cluster. A MongoDB replica set needs an odd number of members for quorum, an etcd or KRaft controller cluster the same, and a Kafka topic needs enough in-sync replicas to survive a broker loss. If each node is very large, you reach your capacity target with fewer nodes, but each node you lose takes a bigger share of the cluster with it. If each node is smaller, the blast radius per failure shrinks but the node count and the coordination overhead grow.
This is why the v3-to-v4/v5 generation choice is a topology decision. A newer generation, or a larger tier like the 2TB XXL v4, packs more capacity into the same node, which tempts you toward fewer, larger nodes. For a stateless worker fleet that is fine. For a quorum-based datastore it is a trade you should make on purpose: fewer large nodes concentrate risk, more moderate nodes spread it. Sizing by role means also sizing the failure domain the role can tolerate, not just the throughput it needs.
What this adds up to
A stateful platform laid out well on OpenMetal is not a single SKU repeated. It is a small set of roles, each placed on the server whose dominant budget matches the role’s dominant demand: core-led Large v4 for the worker fleet, a small Medium v4 for the coordination quorum, memory-led XL for the datastores and the Postgres buffer pool (the 2TB XXL v4 when a single working set outgrows the XL), NVMe-dense Storage nodes for the log and analytical engines, and a node size chosen so the failure domain matches the quorum the role needs. The payoff is money spent on the resource each role actually consumes, and a blast radius you designed rather than inherited.
This is the companion to a separate piece on why the latency-critical real-time bidding path belongs on single-tenant high-frequency bare metal. That one is about one hot path; this one is about laying the whole platform behind it across the catalog.
Talk to an architect
The mapping above is a starting grammar, not a prescription. The right node sizes depend on your working-set sizes, your partition and replica counts, and the failure domain you are willing to operate. OpenMetal architects will size a role-by-role layout against your actual workloads, and a proof-of-concept cluster lets you validate the placement before you commit to node counts. Bring your replica topology and your throughput targets, and pressure-test the mapping against them.
You can also email us at sales@openmetal.io




































