Skip to content
Advertisement

Kubernetes Resource Converter

Convert CPU and memory quantities between every suffix Kubernetes accepts, and catch the ones that mean a billion times what you meant.

Converter Config

Resource

Requests and limits

Canonical form
512Mi what the API server stores and kubectl prints
bytes
536 870 912
KiB (Ki)
524 288
MiB (Mi)
512
GiB (Gi)
0.5
TiB (Ti)
0.000488 rounded — this unit does not divide evenly
kB (k)
536 870.912
MB (M)
536.870912
GB (G)
0.536871 rounded — this unit does not divide evenly
TB (T)
0.000537 rounded — this unit does not divide evenly

The binary units (Ki, Mi, Gi) step by 1024 and the decimal ones (k, M, G) by 1000, so a gibibyte is 73 741 824 bytes more than a gigabyte — about 7% at every step, compounding as the suffix grows.

Request
512Mi
Limit
1Gi
Limit ÷ request

Request is below limit, so this container is Burstable: it can use the headroom when the node has it and is evicted before a Guaranteed pod when it does not.

How Kubernetes Resource Converter works

Kubernetes stores every CPU and memory figure as a single type called a Quantity: a signed number followed by an optional suffix, held internally as an integer with a scale so that no value ever passes through a floating-point representation. Three suffix families share that one grammar. Binary suffixes — Ki, Mi, Gi, Ti, Pi, Ei — multiply by powers of 1024. Decimal suffixes — n, u, m, k, M, G, T, P, E — multiply by powers of 1000. And a bare exponent such as 1e3 is accepted as a third spelling of the same idea.

Because the families overlap in appearance and not at all in meaning, one character decides three orders of magnitude. Lowercase m is milli, a thousandth. Uppercase M is mega, a million. Writing 100m into a memory field asks the scheduler for a tenth of a byte, which it rounds up to one, and the pod is admitted without complaint before dying the moment it allocates anything. Writing 2M into a CPU field asks for two million cores, and the pod stays Pending forever with an event nobody reads. Neither is a syntax error, so neither is caught by a schema check, a linter or an admission webhook.

CPU is the more abstract of the two resources. One unit means one hyperthread on a node — one vCPU on a cloud instance, one core on bare metal — and the kernel enforces it through CFS bandwidth control, handing a container a slice of each 100 millisecond period. That slicing is why millicores are the natural unit: 250m is a quarter of every period, and the scheduler never deals in anything finer than 1m. Memory has no such subtlety. It is counted in bytes, allocated in pages, and a container that exceeds its limit is killed rather than throttled.

The final wrinkle is that a Quantity does not remember how it was written, only which family it belonged to, and re-serialises itself in that family with the largest suffix that still leaves a whole-number mantissa. So 2048Mi comes back from the API server as 2Gi, 1536Mi comes back unchanged because 1.5 is not whole, and 0.25 written into a CPU field comes back as 250m. A manifest that reads differently after a round trip through the cluster has not been altered; it has been canonicalised.

Reference

  • Ki = 2¹⁰ · Mi = 2²⁰ · Gi = 2³⁰ · Ti = 2⁴⁰ · Pi = 2⁵⁰ · Ei = 2⁶⁰
  • k = 10³ · M = 10⁶ · G = 10⁹ · T = 10¹² · P = 10¹⁵ · E = 10¹⁸
  • n = 10⁻⁹ · u = 10⁻⁶ · m = 10⁻³
  • 1 Gi − 1 G = 73 741 824 bytes (about 7.4% more per step)
  • 1 core = 1000m = 1 000 000u; the scheduler’s floor is 1m

How to use this converter

  1. Pick the resource the value belongs to

    Memory and CPU accept identical syntax and punish different mistakes, so the family decides which suffixes deserve a warning rather than a conversion.

  2. Paste the quantity exactly as the manifest spells it

    Including the suffix and any capitalisation. Retyping it from memory is how the one-character difference this tool exists to catch gets normalised away before it is examined.

  3. Read the canonical form first

    That line is what the API server stores and what kubectl will print back, so it is what any diff against the cluster will compare against.

  4. Check the request against the limit

    The multiplier between them decides the QoS class, which decides what gets evicted when a node runs short.

Worked examples

The one-character memory bug

Given
memory: 100m
Result
0.1 bytes — rounded up to 1

Meant as 100 mebibytes, admitted without objection, and fatal at the first allocation. The intended value is 100Mi.

Gibibytes against gigabytes

Given
1Gi compared with 1G
Result
1 073 741 824 against 1 000 000 000 bytes

A 7.4% gap at every step, compounding upward — a node advertised as having 16G of allocatable memory will not fit sixteen containers requesting 1Gi each.

Canonicalisation on the way through

Given
2048Mi
Result
2Gi

Same value, different spelling. A GitOps diff that flags this as drift is comparing what was written against what was stored.

A quarter of a core

Given
cpu: 0.25
Result
250m

Both spellings are accepted, and the cluster echoes the millicore one. A quarter of each 100 ms scheduling period, not a quarter of one core exclusively.

When to use it

  • Reviewing a pull request that changes a resource limit, where the diff shows two suffixes and nothing about the size difference between them.
  • Working out how many replicas of a given request will actually fit on a node whose capacity is advertised in different units.
  • Explaining why a GitOps controller keeps reporting drift on a field nobody has edited.
  • Sizing a JVM heap against a container limit, where the runtime reads megabytes and the manifest was written in mebibytes.
  • Diagnosing a pod that will not schedule, by checking whether the request is a thousand times larger than intended.

Things to watch out for

  • Kilo is lowercase k in this grammar, and K on its own is not a valid suffix at all — the API server rejects it, which at least makes that particular mistake loud.
  • A memory value with a fractional part is rounded up to whole bytes, so 1.5 asks for two bytes rather than failing.
  • Requests decide scheduling and limits decide killing. A pod is placed on a node with room for its request, then throttled or OOM-killed against its limit, so the two answer completely different questions.
  • Setting request equal to limit puts a container in the Guaranteed class, which is evicted last. A request with no limit is Burstable; neither field set is BestEffort, which is evicted first.
  • A quantity is stored in a signed 64-bit integer, so values above roughly 9.2 exabytes are rejected outright rather than wrapping.

Frequently asked questions

What is the difference between Mi and M in a Kubernetes manifest?

Mi is a mebibyte, 1 048 576 bytes, and M is a megabyte, exactly a million. The gap is about 4.9% at that scale and grows with each larger suffix, so a limit written in the wrong family is quietly smaller or larger than intended.

What does 100m mean in a memory field?

A tenth of a byte, because lowercase m is the milli prefix rather than an abbreviation for mega or mebi. Kubernetes accepts it, rounds it up to a single byte, and the container dies as soon as it allocates anything at all.

How many cores is 500m?

Half of one. CPU units count hyperthreads, and the kernel enforces the share by allotting half of every 100 millisecond scheduling period. It is not half a core reserved exclusively; it is a bandwidth ceiling averaged over those periods.

Why does kubectl show a different value from the one I applied?

A Quantity re-serialises itself using the largest suffix in its own family that leaves a whole-number mantissa. The stored value is identical to what was applied; only the spelling changed, which is why 2048Mi is displayed as 2Gi.

Should requests and limits be set to the same value?

Doing so gives a container the Guaranteed quality-of-service class, so it is the last thing evicted when a node runs short of memory. The cost is that headroom the container is not using cannot be borrowed by anything else on that node.

Is there a maximum quantity Kubernetes will accept?

Yes — a quantity is held in a signed 64-bit integer, which caps it near 9.2 exabytes. Beyond that the API server refuses the object rather than silently wrapping, which is one of the few places this type fails loudly.

All devops tools