raatools/

Docker RAM Planner

Plan container memory usage against your available RAM.

Add Container

Services

Custom

RAM Usage 12.5%

Total RAM Used

0 MB

Remaining

3584 MB

System Reserved

512 MB

Fits comfortably

Good headroom for scaling and memory spikes.

What is a Docker RAM calculator?

A Docker RAM calculator estimates the total memory needed to run a set of Docker containers on a server or homelab machine. Each containerized service (database, web server, monitoring, media server) has specific memory requirements. This tool helps you plan your hardware needs by summing up container memory allocations with overhead for the host operating system and Docker engine.

Running out of RAM is the most common cause of Docker container crashes and server instability. Unlike CPU, which can be time-shared, RAM is a hard limit โ€” when a container exceeds its memory allocation, Docker kills it (OOM โ€” Out of Memory). Proper planning prevents unexpected downtime and helps you decide whether to upgrade your server or optimize your stack.

How to use this tool

Add the containers you plan to run and specify their memory requirements. The tool sums the total, adds overhead for the OS and Docker engine, and shows the recommended total RAM. It warns if your planned configuration exceeds typical hardware configurations.

Memory limits vs. memory reservations

Docker gives you two separate knobs for controlling how much RAM a container can use: memory limits and memory reservations. They are often confused but serve very different purposes.

A memory limit (set with --memory or mem_limit in a Compose file) is a hard ceiling. Docker enforces it via the Linux control groups (cgroups) subsystem. The container can never allocate more than this amount โ€” if it tries to, the Linux kernel OOM killer steps in.

  • --memory / mem_limit: the hard upper bound. The container cannot exceed this. Used to protect the host from runaway processes.
  • --memory-reservation / mem_reservation: a soft hint for the scheduler. Docker's swarm scheduler uses this when deciding where to place services. It does NOT prevent the container from using more โ€” it is simply a minimum guarantee that Docker tries to honour.
  • A common best practice: set reservation to the expected idle footprint and limit to the maximum you are comfortable allowing. For example, a container that idles at 200 MB but may spike to 600 MB could be set with reservation=200m and limit=600m.

If you omit both flags, the container has no limit and can consume all available host RAM. On a single-purpose server this may be acceptable, but on a homelab running many services it is a significant risk.

What happens when a container hits its memory limit

When a container's memory usage reaches the value set by --memory, the Linux kernel OOM (Out of Memory) killer terminates the most memory-hungry process inside the container. Docker records this as exit code 137 โ€” a standard POSIX signal 9 (SIGKILL) termination. You will see this in docker ps --all or the container logs as "Exited (137)".

Exit code 137 does not mean your application crashed due to a bug โ€” it means the kernel forcibly killed it because the container hit the RAM ceiling you configured. If you see repeated 137 exits in a stable application, your memory limit is too low, or the workload has grown beyond the original estimate. Increase the limit and re-monitor with docker stats.

Runtime-specific memory considerations

Different language runtimes and database engines manage memory very differently. Setting a container memory limit without accounting for runtime behaviour is one of the most common sources of unexpected OOM kills.

  • JVM (Java, Kotlin, Scala): the JVM allocates a heap upfront. Without explicit flags it may claim a large fraction of available system RAM on startup. Always set -Xmx (maximum heap) to a value below your Docker memory limit โ€” a common guideline is to leave at least 25% headroom above -Xmx for off-heap memory, garbage collection overhead, and native libraries.
  • Node.js: V8 has a default heap limit (around 1.5 GB on 64-bit systems in older versions). If your Docker limit is lower, the process can be OOM-killed before V8 even triggers garbage collection. Use --max-old-space-size (in MB) to cap the V8 old-generation heap and set it below the Docker memory limit.
  • Databases (PostgreSQL, MariaDB, MySQL): databases are aggressive memory users by design โ€” they cache hot pages in their shared buffer pool. PostgreSQL's shared_buffers defaults to 128 MB but is typically tuned to 25% of available RAM for dedicated servers. Inside a container, tune shared_buffers to fit inside your memory limit or the database will be killed unexpectedly under load.
  • Media servers (Jellyfin, Plex): transcoding is highly memory-intensive. A single 4K transcode can require 1โ€“3 GB on top of the idle footprint. If you are enabling hardware-accelerated transcoding, the memory picture changes significantly. Check your server's actual usage under real load before finalising limits.

Common container memory requirements

The figures below are rough starting ranges for planning purposes only โ€” actual usage varies considerably with configuration, dataset size, number of users, and enabled features. Always measure with docker stats after running your actual workload.

  • PostgreSQL: 256MB-1GB+ depending on database size and query complexity.
  • Nginx/Caddy: 50-128MB for typical reverse proxy use.
  • Grafana + Prometheus: 256MB + 512MB-2GB for monitoring stacks.
  • Home Assistant: 256MB-512MB for home automation.
  • Plex/Jellyfin: 1-4GB depending on transcoding and library size.
  • Lightweight reverse proxies (Traefik, Caddy, Nginx): generally 30โ€“150 MB at idle; these are among the most memory-efficient services you can run.
  • Monitoring stacks (Prometheus + Grafana): Prometheus scales with the number of time series it scrapes and retains, from a few hundred MB up to several GB for large environments. Grafana itself is relatively light at around 100โ€“300 MB.

Worked sizing example: a typical homelab Raspberry Pi 4 (8 GB)

To see how RAM allocation adds up, consider a Raspberry Pi 4 with 8 GB running a small self-hosted stack:

  • Host OS (Raspberry Pi OS Lite): ~300โ€“500 MB reserved for the kernel, system processes, and Docker engine itself
  • Pi-hole (DNS ad-blocker): limit 128 MB โ€” lightweight service, low overhead
  • Vaultwarden (Bitwarden-compatible password manager): limit 128 MB โ€” very light Rust binary
  • Home Assistant: limit 512 MB โ€” Python-based, grows with automations and integrations
  • Jellyfin (media server, software transcoding disabled): limit 1024 MB โ€” scales with library size
  • Portainer (container management UI): limit 256 MB

Summing these limits: 500 (host) + 128 + 128 + 512 + 1024 + 256 = approximately 2.5 GB allocated. On an 8 GB device that leaves roughly 5.5 GB of headroom โ€” a comfortable cushion for caching, spikes, and future containers. On a 2 GB Pi 4 this same stack would be overcommitted before even considering spikes. The calculator above performs exactly this kind of arithmetic for any combination of services you choose.

Why over-committing RAM is risky

Over-committing means planning a stack whose combined memory limits exceed the physical RAM available to the host OS. Docker itself does not prevent this โ€” it is perfectly willing to accept a Compose file that allocates 10 GB across containers on a 4 GB machine. The risk materialises at runtime: when actual usage across all containers approaches physical RAM, the kernel begins swapping aggressively. Swap on SD cards and USB drives (common homelab storage) is orders of magnitude slower than RAM and can cause system-wide freezes.

The host OS itself also needs breathing room beyond what any container uses. The Linux page cache, kernel buffers, and system daemons all compete for RAM. As a rule of thumb, never plan a stack that leaves less than 10โ€“15% of total RAM free for the host after summing container limits. On a 4 GB device, that means keeping at least 400โ€“600 MB unallocated.

Memory optimization tips

Set memory limits on all containers (docker run --memory=512m) to prevent any single container from consuming all available RAM. Use Alpine-based images which are smaller and use less memory. Monitor actual usage with 'docker stats' before making final sizing decisions. Leave at least 1-2GB free for the host OS, file caching, and Docker overhead. Swap space can provide a safety net but should not be relied upon for normal operations.

Common mistakes when sizing Docker RAM

  • Setting no limits at all: without --memory limits any container can consume all host RAM, taking down every other service on the machine.
  • Confusing reservation with limit: setting a high mem_reservation does not cap usage โ€” a container with a large reservation and no limit can still exhaust host RAM.
  • Ignoring host OS overhead: allocating 100% of physical RAM across containers leaves nothing for the kernel, Docker engine, and background processes.
  • Sizing from image documentation rather than actual usage: official images often quote minimum requirements. Actual RSS under your real workload can be 2โ€“5x higher. Always verify with docker stats.
  • Forgetting runtime heap settings: containers for JVM or Node applications that have no heap cap set will attempt to use as much memory as the container limit allows โ€” or exceed it and trigger an OOM kill.

Frequently asked questions

How much RAM overhead does Docker itself need?

The Docker engine itself uses about 100-200MB of RAM. The host Linux OS typically needs 500MB-1GB for a minimal server installation. Combined, plan for about 1-1.5GB of overhead before container allocations. On a 16GB server, you realistically have about 14-15GB available for containers.

What happens when a container runs out of memory?

When a container exceeds its memory limit, the Linux kernel's OOM (Out of Memory) killer terminates it. Docker reports this as an exit code 137. Without memory limits, a misbehaving container can consume all available system RAM, potentially crashing other containers and the host OS. Always set explicit memory limits and monitor usage.

Should I configure swap for Docker containers?

Docker allows you to set a swap limit separately with --memory-swap. If you set --memory=512m and --memory-swap=1g, the container gets 512 MB of RAM plus 512 MB of swap. Swap provides a safety net that prevents immediate OOM kills, but relying on swap for normal operation degrades performance significantly โ€” especially on flash storage. Use swap as a last-resort buffer, not a substitute for adequate RAM. On systems without an explicit --memory-swap flag, Docker defaults to twice the memory limit in swap.

How do I find out how much RAM my running containers actually use?

Run docker stats to see a live view of CPU, memory usage, and memory limits for every running container. The MEM USAGE column shows the container's current RSS (resident set size). Compare this against your configured limits to see how much headroom each container has. For a one-time snapshot use docker stats --no-stream. You can also inspect a specific container with docker inspect <name> and look for the MemoryStats field.