RE:NODE
Browse hosting

Sizing14 min read

How much RAM does a WordPress site actually need

Size WordPress by PHP workers, not by guesswork. Where the memory goes, what memory_limit really controls, and how caching changes every number.

0 readers

A small WordPress site - a blog, a brochure site, a portfolio, twenty plugins, a few thousand visits a day - is comfortable in 1 GB. A busy site with a page builder, a membership system or a few hundred products wants 2-4 GB. A WooCommerce shop doing real money with uncacheable cart and checkout traffic wants 4-8 GB and will use it. Those numbers are useless on their own, though, because WordPress does not consume memory as one blob. It consumes memory as a number of PHP processes multiplied by the size of each one, and both halves of that product are things you control. Once you can do that arithmetic you can size any WordPress site in about five minutes, and you can tell in advance whether a bigger plan will help or whether you are about to pay for a plugin problem.

The short answer, by kind of site#

Kind of siteRAMPHP workersThe binding constraint
Blog or brochure, cached, light theme1 GB4-6Almost nothing
Business site, page builder, 25-40 plugins2 GB6-10Worker size
Membership, LMS, forum, logged-in users4 GB10-16Uncacheable requests
WooCommerce, under 1,000 products4 GB10-16Cart and checkout
WooCommerce, large catalogue or heavy traffic8 GB16-30Database, then workers
Multisite network4 GB and upPer siteWhichever site is busiest

Read those as starting points, in the same spirit as the numbers in game server requirements by game: a figure to begin at and correct with real data, not a threshold. The rest of this post is how to do the correcting.

Where the memory actually goes#

On a plan running the whole stack in one allocation, five things want memory and only one of them is WordPress:

  • The web server. nginx is small, a few tens of megabytes. Apache with mpm_prefork and mod_php is not, because each Apache process carries a PHP interpreter.
  • PHP-FPM workers. The big one. Each worker is a full PHP process that loads WordPress core, your theme and every active plugin on every request. This is where most of your memory goes and where all of the useful tuning is.
  • OPcache. A single shared block of memory holding compiled PHP bytecode, sized once and used by every worker. Default 128 MB.
  • The database server. Its own process with its own cache of table pages and indexes. On a small site a few hundred megabytes; on a large shop, as much as you can give it.
  • The operating system, and anything else you run. 150-350 MB for a minimal Linux install before you start.

The important structural point: PHP workers are the only part that scales with traffic. Everything else is roughly fixed. So sizing WordPress is mostly the question of how many workers you can afford and how big each one is.

PHP workers are the real unit#

A PHP-FPM pool runs a fixed number of worker processes. Each one handles one request at a time from start to finish. If ten requests arrive while all your workers are busy, they queue; if the queue is full, visitors get a 502 or a 504. That is what "the site went down under load" almost always is - not memory exhaustion, not the database, just more concurrent requests than workers.

The arithmetic for how many workers you need is short:

code
workers needed = requests per second x average request time in seconds12 req/s x 0.30 s = 3.6  ->  round up, plus headroom  ->  6 workers12 req/s x 1.20 s = 14.4 ->  round up, plus headroom  ->  18 workers

The second line is the whole argument for performance work. The same traffic needs three times the memory when each request takes four times as long, and request time is a property of your theme and plugins rather than of your host.

The arithmetic for how big a worker is comes from the other direction:

code
max_children = (RAM available for PHP) / (average worker RSS)2 GB plan:  2048 - 250 (OS + nginx) - 400 (database) - 128 (OPcache) = 1270 MB1270 MB / 110 MB per worker  =  11 workers

Typical worker sizes, measured as resident memory on a warm process:

  • Plain WordPress, light theme, ten plugins: 40-70 MB
  • Typical business site, page builder, thirty plugins: 90-150 MB
  • WooCommerce, or a heavy multipurpose theme: 150-250 MB
  • One badly behaved plugin importing a large file: whatever memory_limit allows

Set the result in the pool config rather than leaving the default, which is usually far too high for a small plan:

/etc/php/8.3/fpm/pool.d/www.conf
pm = ondemandpm.max_children = 11pm.process_idle_timeout = 20spm.max_requests = 500request_terminate_timeout = 60srequest_slowlog_timeout = 5sslowlog = /var/log/php-fpm-slow.log

pm = ondemand starts workers only when needed and lets them exit when idle, which suits a small site with bursty traffic and keeps the idle memory floor low. pm = dynamic keeps a warm pool and is better for a site with steady traffic. pm.max_requests recycles a worker after a number of requests, which papers over slow memory growth in badly written plugins.

php.ini: memory_limit, OPcache and the settings that matter#

memory_limit is not your plan size and it is not a reservation. It is the ceiling on what one PHP process may allocate before PHP kills the request with a fatal error. Setting it to 512M does not consume 512 MB; it means that one runaway request is allowed to consume 512 MB before being stopped.

That distinction matters because of the interaction: memory_limit multiplied by pm.max_children is your worst case. 512M times 20 workers is 10 GB on a 4 GB plan. In practice workers never all peak together, but a plugin that hits the limit on every request in a loop will find out for you.

WordPress adds its own layer on top, which confuses people who set memory_limit and see no change. In wp-includes/default-constants.php, WordPress defines WP_MEMORY_LIMIT at 40M for a single site and 64M for multisite, and WP_MAX_MEMORY_LIMIT at 256M for the admin area and cron. If the value in php.ini is higher, WordPress uses the higher one; if you need more than 256M inside the admin, you raise the WordPress constant as well:

wp-config.php
define( 'WP_MEMORY_LIMIT', '256M' );define( 'WP_MAX_MEMORY_LIMIT', '512M' );define( 'DISABLE_WP_CRON', true );

Sensible values for a normal site: memory_limit = 256M in php.ini, leave the WordPress constants alone unless something complains. 128M is enough for most sites and is the PHP default; 512M is a diagnostic setting for an import, not a permanent one. If you genuinely need more than 256M to render a page, the page is the problem. PHP settings that matter covers the rest of the file.

OPcache is the highest-value memory you will spend, because it removes the parse and compile step from every request. Two of its defaults are wrong for WordPress and almost nobody changes them:

php.ini
opcache.enable = 1opcache.memory_consumption = 192opcache.interned_strings_buffer = 16opcache.max_accelerated_files = 30000opcache.revalidate_freq = 2

opcache.max_accelerated_files defaults to 10000. A WordPress install with thirty plugins routinely has more PHP files than that, and once the limit is reached the rest are recompiled on every request with no warning anywhere. opcache.memory_consumption defaults to 128 MB, which a large site can fill; when it fills, OPcache flushes everything and starts again, which shows up as a periodic spike in response time that looks like a host problem and is not. Both are worth setting even on a small plan, and the cost is a fixed block of memory shared by all workers rather than per worker.

The database's share#

A WordPress database is usually much smaller than people expect. A blog with a thousand posts is tens of megabytes. Where it grows is in three places: post revisions, wp_options rows marked to load on every request, and for shops, orders and their metadata.

The autoloaded options table is the one that silently taxes every single request, because WordPress loads all of it into PHP memory before anything else happens. Check it:

sql
SELECT ROUND(SUM(LENGTH(option_value)) / 1024) AS kb, COUNT(*) AS nFROM wp_optionsWHERE autoload IN ('yes', 'on', 'auto');

Change the table prefix to match your install. WordPress 6.6 added extra autoload values, which is why the query checks three of them rather than one. Under about 1 MB is healthy. Several megabytes means a plugin, usually one you uninstalled months ago, is leaving data behind, and every request on the site pays for it in both time and memory.

If you run your own database server, its cache of table pages is the setting that matters and everything else is noise: give InnoDB's buffer pool enough room to hold the working set of your tables, and watch that the server as a whole still fits inside the plan. On a managed plan you do not tune this, which is both a limitation and a relief. RE:NODE's web plans include database slots created from the panel with a generated host, user and password, and an "Open in phpMyAdmin" button that signs in with a single-use token expiring in sixty seconds - enough to run the query above and export a dump, not enough to be a permanent workflow. Importing and exporting through phpMyAdmin covers the limits you will hit on a large database.

A separate database plan is worth it once the site is large enough that the database and PHP are fighting each other for the same memory, which in practice is a shop with tens of thousands of orders or a site doing heavy reporting. Connection pools and limits is the other half of that decision.

Caching changes the arithmetic completely#

Every number above assumes PHP runs. A full-page cache means it does not, and that single change is worth more than any plan upgrade you can buy.

The layers, in order of how much they change:

  1. Page cache. The rendered HTML is stored and served without touching PHP or the database. A cached page costs single-digit milliseconds and no worker. This is the difference between needing four workers and needing forty.
  2. OPcache. Compiled bytecode, covered above. Always on, always worth it.
  3. Object cache. Persistent storage for the results WordPress currently recomputes each request. Requires Redis or Memcached to be available, which on most shared PHP hosting it is not - and without it, WordPress falls back to a per-request cache that is thrown away at the end of every page load. Redis, and when you actually need it covers when it is worth arranging.
  4. Browser and CDN caching. Headers, so that repeat visitors and edge nodes do not ask at all. HTTP caching headers explained is the reference.

The catch, and it is a large one: page caching only helps anonymous visitors. Logged-in users, carts, checkouts, forums, member dashboards and anything personalised all bypass the cache by design, because the alternative is showing one person another person's basket. That is why a membership site or a shop needs real worker capacity in a way that a blog with the same traffic does not, and why the table at the top of this post separates them. WordPress speed and caching goes through which plugin does what.

Measuring your own site#

Five minutes of measurement beats any table, including the one above.

Worker size first. On a box where you have a shell, this prints resident memory per PHP process, largest first:

bash
$ ps -eo rss,comm --sort=-rss | grep php-fpm | head -15$ ps -eo rss,comm | grep php-fpm | awk '{s+=$1; n++} END {print s/n/1024 " MB avg"}'

Resident memory overcounts a little, because workers share the pages of the parent process and of OPcache, so treat the average as a safe upper bound rather than an exact figure. Then turn on the FPM status page and watch what the pool is actually doing:

pool.d/www.conf
pm.status_path = /status

listen queue above zero means requests are waiting for a worker. max children reached above zero means you have run out at least once since the last restart. Those two counters answer the "do I need a bigger plan" question directly, and nothing else does.

From inside WordPress, WP-CLI gives you the rest:

bash
$ wp db size --tables --human-readable$ wp plugin list --status=active --format=count$ wp option get cron --format=json | head -c 400$ wp transient delete --expired

Install Query Monitor on a staging copy and load your slowest page while logged in as an administrator. It prints peak memory for that request, the number of database queries, the slowest queries, and any HTTP requests the page made to external services. That last panel is where the real surprises live: a plugin calling a licence server on every page load holds a worker for as long as that server takes to answer, and a worker held for two seconds is a worker not serving anybody.

On a panel-based host the equivalent of the first half is the console graphs, which plot memory, CPU and disk against the plan's limits. Web-server request noise is folded behind a count you can switch off, so the log stays readable while a load test is running.

When to buy more, and when to fix the site#

Buy memory when the measurements say the workers are correctly sized and there are simply not enough of them: the FPM status page shows the queue growing at peak, average worker size is reasonable, and cached pages are fast. That is a real capacity limit and more memory genuinely fixes it.

Do not buy memory for any of these, because it will not help:

  • Slow pages with no traffic. One request taking three seconds on an idle server is a code problem. More memory makes it a three-second request with more memory.
  • A worker average above 200 MB on a normal site. Something is loading far too much. Deactivate plugins in halves on a staging copy until the average drops.
  • Time-outs at exactly 30 or 60 seconds. That is max_execution_time or request_terminate_timeout, not memory.
  • Admin-ajax hammering. A heartbeat or a plugin polling every few seconds consumes workers with no visitors at all. Throttle it.
  • WordPress cron. By default wp-cron.php fires on page loads, so a busy moment also becomes a scheduled-task moment. Set DISABLE_WP_CRON and run it from a real scheduler instead - on RE:NODE that is the Schedules tab with a cron expression and ordered tasks.

One more honest note: WordPress is installed by you, not by us. There is no one-click installer on RE:NODE, and there is no marketplace of pre-baked stacks. What you get is a container with the runtime, SFTP and a file manager, database slots, a proxy slot that issues and renews the certificate once you point an A record at the address shown, and backup slots. Installing WordPress manually is the fifteen minutes that covers, and migrating WordPress to a new host is the version for a site that already exists.

If the site sits behind that proxy slot, remember that PHP sees the proxy's address as the client address unless you tell it otherwise. The real visitor IP arrives in the X-Forwarded-For header, and security plugins, comment logging and rate limiting all read the wrong value until WordPress is configured to trust it. It is the single most common surprise after a move.

FAQ#

Is 1 GB enough for WordPress?

For a blog or a brochure site with page caching on, yes, comfortably. It gives you room for the operating system, a small database, OPcache and four to six PHP workers, which is more concurrency than most small sites ever use. It is not enough for WooCommerce, a membership site, or a page builder with forty plugins.

What should I set memory_limit to?

256M for a normal site. It is a per-request ceiling, not an allocation, so a higher value costs nothing until something actually uses it - but it also multiplies by your worker count in the worst case. If a page genuinely needs more than 256M to render, fix the page rather than raising the number.

Why does my site go down at peak but the memory graph looks fine?

Because you ran out of PHP workers, not memory. Requests queue behind the busy workers and eventually time out as 502 or 504 errors while total memory sits well under the limit. Check max children reached on the FPM status page, and either raise pm.max_children if there is memory spare or make requests finish faster.

Does WooCommerce really need that much more?

Yes, for a structural reason rather than a code-quality one. Carts, checkouts, account pages and admin order screens cannot be page-cached, so every one of them runs PHP and hits the database. A shop with the same visitor count as a blog does several times the real work. WooCommerce hosting requirements goes through the specifics.

Will more RAM make my site faster?

Only if you are currently running out of it. Memory buys concurrency, not speed. A single page load takes the same time on 1 GB and on 8 GB if nothing is contended. What makes a page faster is caching, fewer plugins, fewer database queries and a faster CPU core - see TTFB, Core Web Vitals and hosting for what hosting can and cannot change.

How many visitors can 2 GB handle?

With page caching and a normal theme, tens of thousands of page views a day without noticing. Without caching, and with a heavy builder theme, the same plan might struggle at a few hundred concurrent readers. The honest answer is that the plan size is not the variable that decides it - cache hit ratio and average request time are.


Comments

Completely anonymous: no account, no email, no cookie. We store the name you type, the text and the time - nothing else. Links are limited and markup is not rendered.

0/2000