How Much RAM WordPress Needs: 262 MiB. The Machine Needs 753 MiB.

How much RAM does WordPress actually need?

On our live site the whole stack, Caddy, php-fpm and MariaDB, uses 262 MiB in docker stats (cache-adjusted), and the machine as a whole reports 753 MiB used in free -m. The other 491 MiB is, roughly, memory in use outside the containers (kernel, Ubuntu services, the Docker daemons), a difference between two tools rather than an itemised measurement. One low-traffic instance, 30 August 2026.

Key finding Summed per-process RSS read about 1.9–2× the container figure. Three php-fpm workers plus master summed to 191.4 (recorded as MB; the raw output was not kept) in ps aux, against 97.34 MiB for the whole container in docker stats, a cache-adjusted figure rather than the raw cgroup total. RSS counts shared pages once per process, which most likely explains most of the gap (PSS was not measured), so a pm.max_children formula fed a summed RSS number can inherit that over-count.

Updated 13 October 2026. This page’s summary box still called the docker stats figure the container’s cgroup total, and one sentence called it the container’s actual memory usage; it is the cache-adjusted figure described below, and both now say so. The 2× comparison is now given in both possible units (1.88–1.97×, because the raw ps output was not kept). The explanations for the gap and for the memory outside the containers, here and in the closing summary, are marked as interpretations of one reading rather than separate measurements. No measurement changed.

Updated 8 September 2026. Corrected how this page labelled the docker stats column: on Linux that figure is cache-adjusted, not the raw cgroup total. Added an explicit note that these are observations from one instance rather than a requirement, and stated the MiB to MB conversion.

Our php-fpm container runs three workers and a master. ps aux reports them at 59.0, 58.7, 57.3 and 17.4 MB. That is 191.4 MB in total. docker stats reports the whole container at 97.34 MiB, a cache-adjusted working set rather than the raw cgroup total.

Both readings are correct for what they measure. The first one counts shared memory more than once.

KEY FINDING: on this instance, the summed per-process RSS of php-fpm was about 1.9–2× what docker stats reported for the whole container, most likely because RSS counts a shared page once for every process that maps it (we did not measure PSS, and the docker stats figure also leaves out inactive file cache, which may account for part of the gap). Sizing by a per-worker RSS figure multiplied by the worker count can overstate that component by a similar margin; how much depends on how much of each worker is shared on your machine.

What this page is and is not. These are readings from one low-traffic instance on one day, not a minimum requirement and not a recommendation. A site with plugins we do not run, traffic we do not have, or a different PHP worker count will read differently. Treat the numbers as a worked example of how to measure your own machine, and the sizing advice further down as a separate judgement that we label where we make it. The docker stats and free figures are binary (MiB): 262 MiB is 274.7 MB. The ps figures are shown in MB as recorded; whether those were binary or decimal is not known, because the raw output was not kept (see “Where the 2× comes from”).

Two more numbers from the same machine: the whole WordPress stack (web server, PHP and database) uses 262 MiB, and the memory in use outside those containers comes to 491 MiB: roughly the kernel, Ubuntu’s services and the container runtime, taken as the difference between two tools rather than itemised. That is 1.9× the application.

Measured 30 August 2026, 27 hours after container start.

What the existing guides answer, and where they stop

We read the first page of results for how much RAM does WordPress need. Most of it is recommendation writing from companies that sell hosting. SSD Nodes is representative: 512 MB to 1 GB per site, while stating plainly that “WordPress RAM requirements aren’t officially published.”

One is not like the others. HelpWithWeb publishes real measurements and says how it got them: ps aux RSS readings on live PHP 8.1–8.3 sites with 15 to 40 plugins, under real traffic:

Site type Per php-fpm worker
Plain blog, light plugins 40–70 MB
Business site with a page builder 80–130 MB
WooCommerce / membership 100–200 MB

That is a good measurement of a worker, taken under load across many sites, and this page does not improve on it. Our own ps readings land in the same range: 57–59 MB per worker for a plain blog.

The problem is what happens next. A per-worker figure is only useful if you multiply it by your worker count, and that multiplication is where sizing goes wrong, for a reason that has nothing to do with the measurement being wrong.

What is running

Component Image
Web server / TLS caddy:2-alpine
WordPress wordpress:6-php8.3-fpm-alpine
Database mariadb:11

Host: Amazon Lightsail, $12/month: 2 GB RAM, 2 vCPU, 60 GB SSD, 3 TB transfer (official pricing). Ubuntu, Docker Compose, eleven published articles, near-zero traffic.

That last part matters, and we come back to it.

Measured memory, per container

docker stats --no-stream. One correction to how this page first labelled the column. On Linux the Docker CLI does not print the raw cgroup total: its own documentation says it “reports memory usage by subtracting cache usage from the total memory usage”, where cache is the inactive_file field on cgroup v2. The API returns the unadjusted total and the cache separately, so a client can choose. The figures below are therefore a cache-adjusted working set, which is the number you want when sizing a server, but it is not what memory.current would show you.

Container Using Cap set % of cap
Caddy 44.97 MiB 96 MiB 46.9%
WordPress (php-fpm) 97.34 MiB 320 MiB 30.4%
MariaDB 119.6 MiB 384 MiB 31.1%
Total 261.9 MiB 800 MiB 32.7%

Memory caps are set on each container. Every one is well under its cap, so the caps are not suppressing these numbers. The database is the largest single consumer, and it is the one that grows.

Where the 2× comes from

ps aux on the same machine, php-fpm processes only:

Process RSS
worker 59.0 MB
worker 58.7 MB
worker 57.3 MB
master 17.4 MB
Sum 191.4 MB

The container is using 97.34 MiB. ps reports RSS in kilobytes and the figures above are that value converted; read as MiB (kilobytes divided by 1,024) the sum is 1.97× the container figure, and read as decimal megabytes it is 1.88×. We did not keep the raw ps output, so both are stated: about 1.9–2×.

Nothing is wrong with either tool. RSS is resident set size per process, and it includes shared pages in full for every process that maps them. php-fpm workers are forked from the master: the PHP binary, every loaded extension, the opcache, and any memory the master touched before forking are one physical copy that all four processes report as theirs. ps counts that copy four times. The cgroup accounting behind docker stats counts each physical page once, which is much closer to the RAM you actually have to buy (on Linux the CLI also subtracts inactive file cache from it).

That explanation fits the gap, but it is an interpretation: we did not measure PSS, so how much of each worker’s RSS is shared on our machine is not established here. There is a second, unmeasured contributor: RSS includes file-backed pages such as the PHP binary and shared libraries, while the docker stats figure leaves out inactive file cache, so part of the gap may come from that difference in counting.

What this does to your pm.max_children

The php-fpm tuning guides we read give the same formula:

pm.max_children = available RAM / average process size

The formula is fine. In the guides we read, the input is an RSS reading, and summed RSS counts shared pages more than once. That error propagates in both directions depending on which side you solve for:

  • Sizing a server for a fixed worker count. Ten workers at a 60 MB RSS reading looks like 600 MB. If half of each worker’s RSS were shared, the real figure would be closer to 300 MB, and you would buy up to twice the RAM.
  • Setting max_children for a fixed server. The same inflation makes each worker look expensive, so you cap concurrency lower than the machine can actually support, and requests queue on a box with free memory.

The fix is to measure the process group rather than a process. Three ways, all of which count shared pages once:

# containers
docker stats --no-stream

# systemd services, no containers
systemd-cgtop
cat /sys/fs/cgroup/<slice>/memory.current

# per-process, shared pages split proportionally
smem -k -P php-fpm      # read the PSS column, not RSS

PSS, proportional set size, divides each shared page by the number of processes mapping it. Summing PSS across a process group gives a total you can trust; summing RSS does not.

We did not measure the marginal cost of one additional worker, which is what you would want to plug into the formula. That needs readings at two different worker counts, and we have one. “Half is shared” in the example above is an assumption for the arithmetic, not a measurement of our box and not a constant.

The whole machine

free -m, same moment:

  MiB
Total 1,906
Used 753
Buffers / cache 1,109
Available 1,152

Used is 753 MiB. The containers account for 262 MiB of it in docker stats. The other 491 MiB is, roughly, everything outside the three containers: the kernel, Ubuntu’s services and the container runtime. It is a difference between two tools that count cache differently (free and docker stats), not an itemised measurement. The named processes, by RSS:

Process RSS
dockerd 67.2 MB
systemd-journald 48.8 MB
containerd 38.2 MB
fwupd 26.6 MB
multipathd 26.0 MB
snapd 20.0 MB
networkd-dispatcher 18.1 MB
unattended-upgrade 14.0 MB

(These are RSS figures and carry the same inflation described above, so treat each as an upper bound.)

Docker’s own daemons, dockerd plus containerd, read 105 MB of RSS, an upper bound. That is roughly 40% of what the entire WordPress stack uses. You are paying it for isolation and reproducibility, which may well be worth it. It is simply not free, and none of the sizing guides we read counts it.

Three more entries (fwupd, multipathd, snapd) total 72.6 MB and handle firmware updates, multipath storage and snap packages. A single-purpose web server does not obviously need any of them.

  MiB % of total (1,906 MiB)
Outside the containers (OS, services, runtime) 491 25.8%
WordPress stack 262 13.7%
Available 1,152 60.4%

On this machine, most of the memory in use sat outside the WordPress containers. The advice that says “get 2 GB” is not wrong about the box. It is pointed at the smaller half of the bill.

This also dissolves a contradiction people run into: the measurements are small, the hosting advice is large, and both are correct. They describe different things, and much of the gap between them is everything outside the application.

What this implies for a 1 GB plan

Lightsail’s 1 GB tier is the cheapest step down. Our measured total at idle is 753 MiB, and we have no measurement showing that the share outside the containers would shrink on a smaller instance. On 1 GB, after the kernel’s own reservation, that is roughly 79% of memory used before a single visitor arrives (taking about 953 MiB as usable, the same share of nominal memory our 2 GB instance reports: 1,906 of 2,048 MiB).

That is arithmetic on our numbers, not an experiment. We have not run this stack on 1 GB and we are not going to tell you it works. HelpWithWeb’s decision table says 1 GB is fine for a small blog with proper caching, and caching is precisely what stops workers from multiplying.

Two vCPUs, doing nothing

Load average over 1, 5 and 15 minutes:

load average: 0.00, 0.02, 0.00

Per container: 0.00%, 0.01%, 0.01%. Swap is 2 GB configured and 30 MB used, 1.5%. The machine has never been under memory pressure.

For a content site this size, vCPU is the specification you are least likely to need and the one every plan raises alongside RAM. Our price-per-GB-of-RAM comparison shows how tightly providers bundle the two.

A disk line item to plan for

docker system df:

Type Size Reclaimable
Images 1.75 GB 405 MB (23%)
Build cache 896.6 MB 450.4 MB
Local volumes 309.1 MB 0 B
Containers 57.34 kB 0 B

Build cache is 897 MB, half the size of every image on the machine, and nearly three times the site’s actual data.

Disk in use is 10 GB of 58 GB, so it is not urgent here. On a 40 GB plan with a few rebuilds behind you it stops being a footnote. docker builder prune reclaims 450 MB.

The site’s real data (WordPress files, the database, TLS certificates) is the 309 MB of volumes. Everything else is machinery.

WP_MEMORY_LIMIT is not your memory usage

Our config sets:

define('WP_MEMORY_LIMIT', '256M');

This gets read as “the site needs 256 MB” far more often than it should. It is a per-PHP-process ceiling: the most any single request may allocate before PHP kills it. It is not an allocation, not a reservation, and not a measurement.

The entire php-fpm container, master and workers together, is using 97 MiB. The 256 MB limit has never been approached.

Raising it does not make your site use more memory and does not require a bigger server. It changes when a runaway request fails.

Someone else can own this machinery for you. We turned that premium into an hourly rate for your own time.

Method and limits

What we measured. One site, one configuration, 30 August 2026, 27 hours after container start. docker stats --no-stream for per-container memory, which on Linux is the cgroup total minus inactive_file cache rather than the raw total; ps aux --sort=-rss for per-process RSS; free -m and /proc/meminfo for the host; docker system df for disk; uptime for load. Instance specification confirmed against AWS’s published pricing page. We opened two of the nine first-page results to check their method and did not read all nine in full, so statements about what is unpublished describe our search, not a proof of absence.

The server runs nothing else. We checked, because the instance is named for an earlier project. The only Python processes are networkd-dispatcher and unattended-upgrade, both Ubuntu defaults. Nothing else competes for the 491 MiB.

Traffic. This site gets almost no traffic, and that single fact limits everything above. php-fpm allocates per concurrent request; the container grows with pm.max_children and with how many visitors arrive at once. MariaDB’s InnoDB buffer pool grows with the working set. 262 MiB is a floor for this stack, not a ceiling. For behaviour under load, the per-worker figures cited above are the better source.

Uptime. 27 hours. MariaDB’s memory rises over days as the buffer pool fills. Even the idle number is probably not final.

One configuration. Alpine images, php-fpm rather than Apache with mod_php, no object cache, no page-cache plugin, eleven articles, one theme. Any of those changes the number, and mod_php in particular is heavier.

We did not test 1 GB, and we did not measure the marginal cost of an additional worker. Both of those sections are arithmetic on 2 GB measurements.

What we can say is narrow: on this running WordPress site, summed RSS for php-fpm read about 1.9–2× the container’s own figure, and more memory was in use outside the WordPress containers than inside them (a difference between free and docker stats, not an itemised measurement). If either holds on your machine too, the sizing question has been aimed at the wrong number.

Related

Sources

This page links to each vendor’s own pricing page. No affiliate links are used at this time.