Docker Memory Limit Calculator

Work out the real ceiling. --memory-swap is the total of memory and swap, and it defaults to twice the memory limit, which is the most surprising default in Docker.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Calculate. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A memory limit on its own

The swap total defaults to double, which is almost never intended

-m 512m

Swap disabled

The right setting for a service: hitting the limit is loud rather than slow

-m 512m --memory-swap 512m

Unlimited swap

Never OOM killed, and degrades until the host runs out

-m 512m --memory-swap -1

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Setting -m without --memory-swap

    The container gets twice the limit including swap, so it degrades into swap rather than failing. A slow service is much harder to diagnose than a crashed one.

    Instead:Set --memory-swap equal to --memory. Hitting the limit then produces an immediate, obvious OOM kill.

  2. Not telling the runtime what the limit is

    A JVM or a Node process that sizes its heap from the host's memory will exceed a container limit and be killed, while its own numbers look reasonable.

    Instead:Set -XX:MaxRAMPercentage or --max-old-space-size from the container limit, leaving headroom for everything outside the heap.

  3. Reading 512m as 512 megabytes

    Docker's suffixes are binary, so it is 536,870,912 bytes. Translating to a Kubernetes manifest as 512M silently shrinks it.

    Instead:Use Mi and Gi in Kubernetes to mean the same thing.

-m on its own gives the container twice what it says

--memory-swap is not the swap allowance. It is the total, and when unset it is twice --memory. So -m 512m permits 512 MiB of RAM and a further 512 MiB of swap.

Why the default is worse than it sounds

A leaking process does not crash at the limit. It slides into swap, where it is not killed and not healthy, just extraordinarily slow. The symptom is a service that has not failed and is not responding, which is far harder to diagnose than a crash. Setting --memory-swap equal to --memory turns that into an immediate, loud OOM kill.

An OOM kill gives the process no chance to react

The kernel ends the largest process in the cgroup immediately. No signal handler, no flush, no final log line. The application's log stops mid-sentence, which is itself the clue, and the container exits 137.

137 does not always mean memory

docker stop escalating to SIGKILL produces the same code. docker inspect --format '{{.State.OOMKilled}}' is the deciding field.

Runtimes that size their heap from the host will exceed this

A JVM before 10, or one without container support enabled, reads the host's total memory and takes about a quarter of it as a heap. On a 64 GiB host inside a 512 MiB container that is a 16 GiB heap, so the process grows until the kernel kills it while believing it is being conservative. Node has the same problem with its default old-space size.

The limit covers the whole process, not the heap

So a heap sized at the container limit leaves nothing for thread stacks, metaspace, native buffers or the runtime itself. Around 75% is a workable starting point, and it needs measuring rather than assuming.

The suffixes are binary

512m is 512 MiB, not 512 MB. Kubernetes spells the same value 512Mi and uses 512M for the decimal megabyte, which is where the two get confused when a manifest is translated by hand.

More docker tools

Docker Container Image Reference Parser Is that a registry or a Docker Hub user? Dockerfile Linter What this image will do to you later docker run to Kubernetes YAML And the flags that have no equivalent Docker Compose to Kubernetes And why depends_on has no equivalent Docker Logging Driver Generator json-file never rotates Dockerfile Non-Root USER Generator Numeric, or Kubernetes rejects it Docker OCI Image Labels Generator One of them actually does something Docker Hub Pull Rate Limit Calculator Counted per IP, not per person Docker Registry Storage Calculator Deleting a tag frees nothing Docker Build Context Analyzer All of it uploads before anything runs Docker Compose to docker run Converter Names stop resolving Docker Compose Version Migrator version: is obsolete now Docker Compose Network and Port Viewer Everything reaches everything Docker Command Explainer Which flags hand over the host Docker BuildKit Cache Mount Generator apt deletes the cache you just mounted Docker BuildKit Secret Mount Generator ARG is in docker history forever Docker .dockerignore Pattern Tester *.log does not match logs/app.log Docker Image Manifest Viewer unknown/unknown is not broken Docker Image Config Viewer Anyone who can pull can read your Env Docker Registry Token Decoder Check the scope before the credentials Docker HEALTHCHECK Generator Docker does nothing when it fails Docker Network Subnet Calculator 31 networks, then it stops Docker History Analyzer The rm did not save anything Docker Run to Compose Converter Some flags have no equivalent Docker CMD and ENTRYPOINT Tester Why docker stop takes ten seconds Docker Port Mapping Tester -p reaches past your firewall Docker -v to --mount Converter -v invents missing directories Docker Restart Policy Simulator The backoff resets after ten seconds Docker Exit Code Explainer 137 is two different problems Docker CPU Limit Converter --cpu-shares limits nothing Docker Compose Interpolation Tester An unset variable is an empty string Docker Environment Variable Precedence Tester Your shell does not reach the container Docker Compose Override Merge Previewer An override can add a port, never remove one Dockerfile Multi-Stage Build Analyzer Which stages reach the image Dockerignore Validator It is not .gitignore

Elsewhere on the site