deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Kubernetes accepts 512m as a memory limit, and it means half a byte

A dev.to experiment shows 512Mi, 512M and 512m all pass kubectl apply yet mean 536,870,912 bytes, 512,000,000 bytes and 0.512 bytes. The failure modes include silently wrong capacity planning.

Kubernetes accepts 512m as a memory limit, and it means half a byte

A walkthrough published on dev.to highlights a trap buried in Kubernetes resource syntax: three memory values that look nearly identical in a manifest — 512Mi, 512M and 512m — are all accepted by kubectl apply, but they mean 536,870,912 bytes, 512,000,000 bytes and 0.512 bytes respectively. Rather than arguing from the documentation, the author built a throwaway kind cluster running Kubernetes 1.37 with containerd and cgroup v2, and applied every malformed quantity they could think of to see what actually happens.

Binary and decimal suffixes are not interchangeable

Kubernetes quantities accept both binary suffixes (Ki, Mi, Gi — powers of two) and decimal ones (k, M, G — powers of ten). According to the dev.to post, the discrepancy grows with the unit: M is 4.6% smaller than Mi, and G is 6.9% smaller than Gi. A JVM tuned for a 4Gi heap that receives a 4G limit silently loses 281 MiB of headroom — the kind of OOMKill that only appears under load, on a Friday, in a service nobody touched.

The lab makes the gap visible. On a 7.65 GiB kind node, a pod with a 4G memory limit started normally, while an identical pod requesting 4Gi could not be scheduled at all due to insufficient memory. At the cgroup level, containerd wrote 500M as 499,998,720 bytes (rounded down to a 4 KiB page) and 500Mi as 524,288,000; a 4G limit became 3,999,997,952 bytes. The author also flags a display mismatch worth remembering: kubectl describe node reports capacity in Ki, cloud providers sell RAM in GiB, and many dashboards show GB — hence the advice to pick binary suffixes and stay with them.

Lowercase m is milli, even for memory

On CPU fields, m is the standard unit — 500m is half a core. Applied to memory it still means milli, so 512m is roughly half a byte. Kubernetes 1.37 emitted a warning that the fractional byte value was invalid and must be an integer, and then created the pod anyway. The warning does not appear with --dry-run=client, so it scrolls past easily in a CI log or a Helm upgrade, and the object is stored exactly as written. Stranger still, memory: 2000m raises no warning at all because it resolves to a whole number of bytes — two of them.

What a half-byte does in practice

As a request with no limit, the author found the pod starts within seconds: the scheduler counts it as zero and the cgroup gets no memory ceiling, so the only symptom is capacity planning that is quietly wrong. One odd side effect: a single 512m pod makes kubectl describe node render the node's entire memory row in milli-bytes — 304087041536m, or about 3%, in the test — which is a reliable sign that someone typed a lowercase m.

As a limit, there is no OOMKill because there is nothing to kill. The pod's cgroup gets a memory.max of zero, and it hangs in ContainerCreating with a FailedMount error — no space left on device — while writing a roughly 2 KB certificate file. The service-account token volume is a tmpfs, tmpfs writes are charged to the pod's memory cgroup, and that budget is zero bytes. Disabling token automount moves the failure to the container runtime, which cannot apply the cgroup configuration, with the node journal logging a memory allocation error. None of these messages point back at the YAML.

CPU rounds up, memory stays broken

The post also corrects a common belief: Kubernetes does accept CPU precision finer than 1m — it rounds up silently. The author's tests show 0.0001 stored as 1m, 0.0015 as 2m and 100.5m as 101m. Harmless for CPU, but the same parser is what keeps 512m of memory as written. Canonicalisation adds diff noise: 0.5Gi is stored as 512Mi, 0.5G becomes 500M, and 512.5Mi comes back as 524800Ki, which matters if you diff live objects against Git.

How to catch it first

The recommended defences: lint in CI for a digit followed by m under memory fields (a one-line grep, or a Kyverno or Gatekeeper policy at admission time); run kubectl apply with --warnings-as-errors, which would have blocked the 512m pod; and standardise on Mi and Gi for memory in the contributing guide, letting the linter enforce it. When a pod is stuck in ContainerCreating with a baffling storage error, the author's advice is to check its memory limit before checking the disk.

Why it matters

These typos survive review precisely because the difference is invisible to a human eye and tolerated by the API server. The consequences are indirect and delayed: workloads under-provisioned by several percent that only fail under peak traffic, nodes whose capacity math is silently wrong, or a container that never starts while the error message complains about disk space. Because dry runs and casual review both miss the warning, prevention has to be automated — a lint rule, an admission policy, or failing CI on warnings. Standardising a team on binary suffixes costs one line in a contributing guide; debugging a half-byte memory limit in production costs far more.

  • #kubernetes
  • #devops
  • #cloud
  • #containers
  • #yaml

Related posts