Operations

Choosing instance sizes: matching CPU, memory and storage to a workload

A right-sizing method that works on any provider: profile the workload, find the binding resource, pick the family, then resize from measured data.

Updated September 25, 2026

Choosing instance sizes: matching CPU, memory and storage to a workload

Most instance-sizing mistakes come from starting with the price list. Engineers scan the plans, pick something that looks reasonable and discover months later that the server is either idle or swapping. A better approach starts with the workload and ends with the plan. This guide describes that method without relying on any vendor’s benchmark numbers, using Hetzner and UpCloud as examples of how plan families are organized.

Step 1: find the binding resource

Every workload is limited by one resource before the others. A web application with a small database is usually memory-bound. A build server is CPU-bound. A database with many small writes is bound by storage IOPS. A media pipeline is bound by disk throughput or network. Identify which one binds first, because that is the dimension to size generously; everything else can be modest.

  • CPU-bound: compilers, video encoding, request-heavy APIs with little state
  • Memory-bound: caches, in-memory databases, JVM services, most web stacks
  • IOPS-bound: relational databases, message queues, search indexes
  • Throughput-bound: backups, log shipping, large file transfers

Step 2: choose shared or dedicated CPU

Providers split virtual machine families by whether CPU time is shared with neighbors or reserved. Hetzner offers shared-resource and dedicated-resource cloud instances plus ARM-based options. UpCloud organizes its Cloud Servers into Starter, Premium and Cloud Native plan families. Kamatera, for comparison, labels the same idea as CPU types A, B, D and T.

The rule of thumb: shared CPU suits development, staging and any production service that tolerates variable performance. Dedicated CPU suits databases, latency-sensitive APIs and anything that runs hot for long periods. If you cannot decide, start shared, measure steal time, and move up if it is consistently high.

Step 3: size storage by behavior, not capacity

Disk capacity is the easy number. What actually decides performance is how the disk is accessed. UpCloud tiers its block storage into MaxIOPS, Standard and Archive, which maps directly onto the question of how often data is read and written. Hetzner cloud instances use local NVMe with the option to attach additional volumes.

Access pattern Storage choice Example
Constant small random I/O Highest IOPS tier or local NVMe Primary database
Occasional reads, steady growth Standard block storage Application logs, uploads
Rarely read Archive tier or object storage Old backups, exports

Keep the operating system and application on fast storage and push everything cold to a cheaper tier. This is also how the storage options guide recommends thinking about block, object and backup storage.

Step 4: start one size smaller than instinct says

Because cloud instances resize, the cost of starting small is a few minutes of downtime later. The cost of starting large is paying for idle capacity every month. Deploy one size below your estimate, run a representative load for a week, and look at three numbers: peak CPU utilization, peak memory including cache, and disk wait time. Resize only the binding dimension.

Account limits shape the plan

New accounts often cap the number of servers rather than their size. The marketplace lists the Hetzner 5 Server Limits Account and Hetzner 10 Server Limits Account on the Buy Hetzner Account page; with a five-server cap, a few well-sized machines beat many small ones. The UpCloud $500 Credit Account listing gives room to test a couple of plan families against real traffic before settling. Read vCPU limits and service quotas for how caps are counted.

Step 5: revisit quarterly

Workloads drift. A service that was memory-bound at launch becomes CPU-bound after a caching layer is added. Put a recurring reminder to review utilization every quarter and to move instances between families as the profile changes. Vertical resizing is cheap on both Hetzner and UpCloud; leaving a wrong size in place for a year is not.

A worked example

A small SaaS backend: a Node.js API, a Postgres database and a Redis cache. The API is CPU-light and memory-moderate, so a shared-CPU plan with generous memory fits. Postgres is IOPS-bound, so it gets a dedicated-CPU plan on the fastest storage tier. Redis is memory-bound and small, so it shares the API host. Three services, two machines, and every dollar is spent on the dimension that binds.

Questions on this topic

How do I know whether a server is CPU-bound or memory-bound?

Watch it under realistic load for a few days. If CPU utilization sits near its ceiling while memory has headroom, it is CPU-bound. If memory including cache is exhausted and the system swaps or the kernel evicts cache aggressively, it is memory-bound. High I/O wait with idle CPU points to storage. Most monitoring agents expose all three.

When is dedicated CPU worth paying for?

When the workload runs hot for long periods or is sensitive to latency spikes. Databases, real-time APIs and build farms qualify. Development environments, staging and low-traffic sites usually do not. On Hetzner the choice is between shared-resource and dedicated-resource instances; on UpCloud it maps to plan families. Start shared and move up if steal time is consistently high.

Does a server-limit account change how I should size instances?

Yes. With a cap such as the Hetzner 5 Server Limits Account, each server slot is scarce, so favor fewer, larger, well-tuned machines over many small ones. Combine light services on one host and reserve separate slots for the database and anything that must scale independently. See the Hetzner server limits guide for details.

More from the Blog

All articles