MiB vs MB: Why Kubernetes, Docker, and JVM Use Binary

The xconvert Mebibytes to Gigabytes converter showing the conversion between binary and decimal storage units

A Kubernetes manifest with memory: 256M and one with memory: 256Mi describe pods that get 5% different memory allocations. A JVM started with -Xmx2g actually gets 2 GiB (2.147 × 10⁹ bytes), not 2 GB. docker images prints sizes in MB; docker-compose images prints them in MiB. Engineering tools mix decimal and binary prefixes inconsistently — usually invisibly — until a pod gets OOMKilled because someone wrote M when they meant Mi. This guide covers the conventions used by the major developer tools, the traps that catch teams in production, and the unit each tool actually uses under the suffix.

Quick answer: 1 MiB = 1,048,576 bytes (2²⁰); 1 MB = 1,000,000 bytes (10⁶) — a 4.86% difference. Kubernetes: M = MB (decimal), Mi = MiB (binary). JVM: m / M / g / G = MiB / GiB (binary, despite the letter). Docker: decimal in docker images, binary in docker-compose images. cgroups: raw bytes. Always specify with the i (Mi, Gi) for explicit binary.

Jump to a section

The 4.86% gap and why it bites in production

Two definitions:

The gap grows with each scale-up:

  • KiB vs KB: 2.4% difference
  • MiB vs MB: 4.9% difference
  • GiB vs GB: 7.4% difference
  • TiB vs TB: 10% difference

The IEC ratified the binary prefixes (kibi/mebi/gibi/tebi) in ISO/IEC 80000-13 specifically to end the ambiguity. Adoption in developer tooling is uneven — some tools follow the spec rigorously (Kubernetes, Prometheus), some quietly use binary while displaying decimal-looking suffixes (JVM, Linux df -h), and some are internally inconsistent (Docker).

The 4.9% MB-vs-MiB gap rarely matters for a 1-byte difference, but for a tightly-tuned container or JVM with limits set near actual usage, the gap is enough to either crash the workload or leave free memory unused on the host. For a deeper general treatment of binary vs decimal storage prefixes (including the hard-drive 1 TB → 931 GB puzzle), see Bytes to GB: Binary vs Decimal Storage Units Explained.

The xconvert Mebibytes to Gigabytes converter showing the conversion between binary and decimal storage units

Kubernetes: M vs Mi, and the OOMKilled trap

The Kubernetes resource-management docs define two distinct memory suffix conventions:

SuffixMeaningBytes per 1 unit
kkilobyte (decimal)1,000
Mmegabyte (decimal)1,000,000
Ggigabyte (decimal)1,000,000,000
Kikibibyte (binary)1,024
Mimebibyte (binary)1,048,576
Gigibibyte (binary)1,073,741,824

So:

The trap: if you’re sizing a JVM service to “256 MB heap,” writing memory: 256M gives the container 244 MiB of memory — less than the 256 MiB heap the JVM allocates with -Xmx256m (see next section). The pod starts, the JVM tries to allocate its heap, and gets OOMKilled because the container limit is below the JVM’s requested heap.

Recommended practice for memory limits in K8s: always use the Mi / Gi (binary) suffix. RAM is fundamentally binary; the SI decimal suffixes (M/G) are technically valid but invite the mismatch error above. K8s docs say either is allowed but explicitly recommend binary for memory.

CPU limits don’t have this problem (they’re a different unit — fractional CPU cores 0.5, 100m = 0.1 CPU, etc.), but memory is where the M-vs-Mi distinction is load-bearing.

JVM heap flags: -Xmx uses binary despite K/M/G suffixes

This is the trap that catches Java developers most often. From the Oracle HotSpot ergonomics docs:

The single-letter suffixes (k, m, g, K, M, G) all use binary multipliers in the JVM, even though those letters are the SI decimal symbols everywhere else. The JVM has had this convention since at least JDK 1.4 and shows no sign of changing.

Why this is a trap: a developer reading -Xmx256m who’s been reading Kubernetes docs assumes that’s 256 MB (decimal). It’s actually 256 MiB — about 5% more memory than they thought. Set a Kubernetes container limit of memory: 256M for a JVM with -Xmx256m, and the container gets killed.

Recommended fixes:

Modern JVMs (JDK 11+ container-aware mode) read the cgroup memory limit at startup and right-size their heap relative to that — set the container limit, leave -Xmx unset, and the JVM picks a sensible default (usually 25% of cgroup limit for the heap). For tightly-tuned services, still set -Xmx explicitly and leave at least 25–50% headroom between heap and container limit for non-heap memory (thread stacks, metaspace, direct buffers, native libraries).

Docker: inconsistent reporting across CLI tools

Docker is the messiest case — different CLI tools in the same ecosystem use different conventions:

ToolConventionExample output
docker imagesDecimal (MB, GB)nginx 142MB
docker-compose imagesBinary (MiB, GiB)nginx 135MiB
docker statsBinary (MiB, GiB)MEM USAGE 245.7MiB / 1.952GiB
docker inspectRaw bytes (no formatting)"Size": 148723456
Docker Hub web UIDecimal (MB)“Compressed Size: 54.3 MB”
Container Registry APIRaw bytes{"size": 148723456}

So the same image can read as 142 MB in docker images, 135 MiB in docker-compose images, and 148,723,456 bytes in docker inspect. All three are technically the same number — 142,000,000 ÷ 1,048,576 ≈ 135.5 and 148,723,456 ÷ 1,048,576 ≈ 141.8 (the small extra difference is rounding).

For image size budgets (Docker Hub auto-pull caps, deployment manifests with size thresholds), the convention depends on the system enforcing the cap. Docker Hub’s “free tier 100 GB egress” is decimal GB. A kubectl describe pod image-pulled error reporting a 1 GiB limit is binary GiB. Read which tool reported the number before doing the math.

The Docker community has tracked this inconsistency for years (see GitHub docker/cli issue #3091) — it’s not going away. The pragmatic workaround is to standardise on bytes for any automation and let the display layer decide which suffix to show.

cgroups: raw bytes in memory.max

Below Kubernetes, the Linux kernel’s cgroup v2 controller stores memory limits as raw bytes in /sys/fs/cgroup/.../memory.max. No suffix, no conversion:

That’s 536,870,912 bytes = exactly 512 MiB = 512 × 2²⁰. When Kubernetes sets a memory: 512Mi limit, it writes this exact byte count to the cgroup. When you set memory: 512M instead, Kubernetes writes 512,000,000 — about 488 MiB of actual memory.

Implication for observability: any tool reading cgroup memory limits directly (Prometheus cadvisor, kubectl top, docker stats) operates on raw bytes and then converts for display. The conversion choice (MiB vs MB) is purely cosmetic; the underlying value is the byte count. When debugging “the pod has more / less memory than I configured,” diff against the byte value, not against the suffix.

Prometheus metric naming conventions

Prometheus naming conventions recommend base SI units for metric values — and for memory, that means bytes, no prefix:

The Prometheus rationale: display tooling (Grafana, Mimir, AlertManager) handles unit conversion at presentation time; storing in base units avoids ambiguity. A Grafana dashboard then converts to MiB/GiB for display using humanize1024(value) or humanizeBytes(value).

Don’t write metrics like node_memory_MemTotal_MiB — even though it’s tempting for readability, the canonical Prometheus approach is _bytes with the conversion happening downstream. Tools that emit _mb or _gb metrics are non-canonical and will trip up automation that expects bytes.

Quick reference for the common developer tools

Tool / contextDefault unitNotation example
Kubernetes memory limitBinary (Mi, Gi) recommended256Mi, 2Gi
Kubernetes memory limit (decimal)Allowed but error-prone256M, 2G (= 244 MiB, 1.86 GiB)
JVM -Xmx, -Xms, -XssBinary (despite single-letter suffix)-Xmx2g = 2 GiB
docker imagesDecimal142MB
docker-compose imagesBinary135MiB
docker statsBinary245MiB / 1.95GiB
kubectl top podsBinary245Mi
cgroup memory.maxRaw bytes536870912
Prometheus *_bytes metricsRaw bytes268435456
Linux free -mBinary (MiB labelled as Mi or M)Mem: 15920
Linux df -hBinary (labelled with no i)4.0G, 2.0M
AWS EBS volume sizesBinary (GiB billed as “GB”)“10 GB volume” = 10 GiB
Cloud cost-per-GBMixed — check provider docsAWS: GiB; some GCP: GB

Bookmark this if you work across multiple stacks — the conventions differ even within the same vendor (Docker tools).

Use the xconvert MiB ↔ MB converter

For precise conversions — debugging a container size mismatch, sizing a JVM heap relative to a K8s limit, converting Prometheus byte metrics to human-readable values — use:

For the general-purpose treatment of binary vs decimal across all storage scales (KB / MB / GB / TB and the “why does my hard drive show less” puzzle), see the companion article: Bytes to GB: Binary vs Decimal Storage Units Explained.

Related explainer articles on the xconvert blog:

FAQ

Should I use Mi or M for Kubernetes memory limits?

Use Mi (binary). It matches what your kernel, container runtime, and JVM actually allocate. The K8s docs note both work, but using M (decimal) introduces a 4–7% gap that can cause OOMKilled events when the workload’s actual memory allocation lands between the configured M and the equivalent Mi. Stick with 256Mi, 2Gi, 512Mi for memory; reserve m (lowercase) for CPU millicore notation (500m = 0.5 CPU).

Why does the JVM use single-letter suffixes that mean binary?

Historical. Java’s -Xmx flag predates the IEC binary prefixes (1998). The JVM has used k/m/g to mean binary since Java 1.4 (2002), and changing it now would break thousands of production scripts. Modern JDK documentation specifies the binary meaning explicitly — but if you only read the flag and not the docs, you’d assume decimal.

What is 256Mi in plain decimal megabytes?

256 MiB = 256 × 1,048,576 = 268,435,456 bytes = 268.43 MB (decimal). So memory: 256Mi in Kubernetes is equivalent to about 268M (decimal). The 12 MB gap (about 5%) is the source of most “I set the right limit, why did it OOMKill” issues.

Does AWS bill EBS in GB or GiB?

AWS bills EBS in GiB but labels it “GB” on the pricing page. A 10 “GB” EBS volume holds 10 × 2³⁰ = 10.74 × 10⁹ bytes. The AWS billing docs are explicit on this point (Amazon EBS pricing page); the “GB” label is a convention they keep for backward compatibility. Same convention applies to RDS storage, EFS, S3 — see AWS S3 FAQs for the formal statement.

How do I check what unit Docker is using for a specific image?

Use docker inspect <image> --format '{{.Size}}' — returns raw bytes, unambiguous. Then convert with the xconvert bytes to MB tool (decimal) or bytes to mebibytes (binary) depending on what convention you need to match.

Is kubectl top pods showing MiB or MB?

Binary (MiB, GiB) — but the suffix is rendered as Mi / Gi without the redundant B. So 245Mi means 245 MiB. The numeric value matches the cgroup memory.usage_in_bytes divided by 2²⁰.

What about CPU — is “100m” decimal or binary millicores?

Neither — CPU notation in Kubernetes is fractional cores. 100m = 100 millicores = 0.1 CPU; 1000m = 1 CPU. There’s no binary/decimal ambiguity for CPU because the unit is decimal-fractional by definition. Lowercase m (millicore) is different from uppercase M (megabyte) — confusing but well-defined.

Sources

Last verified 2026-05-25.

By James