A WooCommerce shop with a few hundred products, under twenty orders a day and no live traffic spikes runs comfortably on 2 GB of memory and one and a half CPU cores. A shop doing a few hundred orders a day wants 8 GB and three or four cores. Those two numbers bracket most of the shops anybody reading this is about to build, and the gap between them is not really about traffic - it is about how much of the site can be served from a cache, and a shop is the kind of site where the answer is "less than you think".
That is the difference worth understanding before you buy anything. A brochure site or a blog can serve every page from a static cache, and a 1 GB plan will hold up under a surprising amount of load. A shop cannot cache the cart, the checkout, the account pages or the stock counts, so every one of those requests runs PHP, hits the database, and occupies a worker for as long as it takes. Sizing a shop means sizing the uncacheable part.
What WooCommerce needs before you count RAM#
The software requirements are short, and WooCommerce checks most of them itself. Open WooCommerce, then Status, in the admin: the System Status report grades your server against the current release's expectations and flags anything it does not like in red. That page is more up to date than any article, including this one, so treat the table below as the shape of the answer and the page as the authority.
| Requirement | Works | Comfortable |
|---|---|---|
| PHP | 7.4 | 8.2 or 8.3 |
| Database | MySQL 5.7, MariaDB 10.5 | MySQL 8, MariaDB 10.11 |
| WordPress memory limit | 256 MB | 512 MB |
| HTTPS | Required | Required |
| PHP max input vars | 1000 | 5000 |
HTTPS is not optional for a shop. Payment gateways refuse to load their scripts over plain HTTP, browsers will not autofill a card field on an insecure page, and the checkout will quietly fail in ways that look like a plugin bug. Get the certificate working first - HTTPS and Let's Encrypt explained covers the issuance, and www vs apex domain and redirects covers making sure both hostnames are covered, because a shop that errors on www loses the customers who typed it.
The PHP extensions that actually matter are mysqli, curl, mbstring, dom, xml, json, zip, fileinfo, openssl, intl, bcmath and one of gd or imagick for image resizing. A few payment and shipping plugins want soap. Missing curl breaks every gateway; missing gd or imagick means product images are never resized and your media library grows at ten times the rate it should.
The PHP settings themselves are the part people get wrong, and they are worth setting explicitly rather than inheriting:
memory_limit = 512Mmax_execution_time = 300max_input_vars = 5000upload_max_filesize = 64Mpost_max_size = 64Mopcache.enable = 1opcache.memory_consumption = 256opcache.interned_strings_buffer = 16opcache.max_accelerated_files = 20000opcache.revalidate_freq = 2max_input_vars is the strange one. WooCommerce settings screens and variable products submit enormous forms, and when the limit is hit PHP silently truncates the input - so you save a product with 90 variations and 40 of them vanish with no error anywhere. 5000 is the number WooCommerce itself asks for.
opcache.max_accelerated_files defaults to 10000, and a WooCommerce install with twenty plugins has more PHP files than that. Once the cache is full, the overflow is recompiled on every request and your time-to-first-byte goes up for no visible reason. PHP settings that matter goes through the rest.
WordPress has its own limit on top of PHP's, and it is lower by default:
define( 'WP_MEMORY_LIMIT', '256M' );define( 'WP_MAX_MEMORY_LIMIT', '512M' );Sizing: RAM, CPU and disk by order volume#
Orders per day is a better input than page views, because an order is the expensive request and most page views are cached.
| Shop | Orders / day | RAM | vCPU | Disk |
|---|---|---|---|---|
| Launch, small catalogue | under 20 | 2 GB | 1 - 1.5 | 10 GB |
| Growing, regular traffic | 20 - 100 | 4 GB | 2 | 25 GB |
| Busy, campaigns and sales | 100 - 500 | 8 GB | 3 - 4 | 50 GB |
| Larger than that | 500+ | Own machine | 6+ | 100 GB+ |
Three modifiers push a shop up a row regardless of order count. Catalogue size: 20,000 products with variations puts real weight on the database and on every admin page, even at low order volume. Plugin count: each active plugin is code loaded on every request; forty plugins is a different application from ten. Traffic shape: a shop that gets its month's orders in two hours during a sale needs to survive the peak, not the average.
Disk is mostly images. Budget roughly 2 - 5 MB per product once WordPress has generated its thumbnail sizes, plus the database, plus backups if you keep them locally - which you should not.
On RE:NODE, the CMS line under web hosting runs from 1 GB to 8 GB with up to four vCPU, which covers the first three rows of that table. CPU is a hard throttle to the share you bought rather than a burst allowance, so a shop that sits at 100% during a sale is slow, never suspended. Above the top row, a shop wants its own machine - see choosing between a VDS and a panel for what changes when you take on the operating system yourself.
PHP workers are the real concurrency limit#
Memory does not get consumed by "traffic". It gets consumed by PHP worker processes, each one handling one request at a time. The number of workers is what decides how many uncacheable requests you can serve at once, and it is set in the PHP-FPM pool:
pm = dynamicpm.max_children = 12pm.start_servers = 4pm.min_spare_servers = 2pm.max_spare_servers = 6pm.max_requests = 500A WooCommerce request typically holds 64 - 128 MB of PHP memory, so the arithmetic is: memory available to PHP, divided by the average process size, is your ceiling for pm.max_children. On a 4 GB plan with roughly 3 GB free for PHP and 96 MB per process, that is around 30 - and you would set it lower, because the database and the web server need memory too.
Setting it too high is worse than setting it too low. Too low means requests queue, which is slow. Too high means the kernel runs out of memory, and on RE:NODE that means the container is stopped and restarted clean rather than left to swap - a fast failure instead of a slow one, but a failure. Size the workers to the memory you have.
pm.max_requests = 500 recycles each worker after 500 requests, which papers over slow memory leaks in plugins. It is not a fix, but it is a cheap safety net.
The other thing to know: a checkout request holds its worker for the entire round trip to the payment gateway. If the gateway takes three seconds, that worker is gone for three seconds. Twelve workers and a slow gateway is a queue, which is why a shop feels fine in testing and falls over on the first campaign.
Why a shop cannot be fully page-cached#
Full-page caching is what makes WordPress fast, and it has to be switched off for exactly the pages that make money. These must always bypass the cache:
/cart/,/checkout/and/my-account/, plus any page containing those blocks or shortcodes under a different URL.- Any request carrying the
woocommerce_items_in_cartorwoocommerce_cart_hashcookies, or a cookie whose name startswp_woocommerce_session_. A visitor with something in their basket must never be served another visitor's cached page. - Any request with
add-to-cart,wc-ajaxorremoved_itemin the query string.
Every serious caching plugin knows these rules and applies them by default. The danger is a hand-rolled rule at the proxy layer, or a CDN configured to "cache everything", which is how a shop ends up showing one customer another customer's basket. If you take one thing from this section, take the cookie list.
Cart fragments are the related performance trap. By default WooCommerce makes an uncached AJAX request to ?wc-ajax=get_refreshed_fragments on every single page load, so the cart widget can show the right count - which means every page view, cached or not, still runs PHP once. On a shop whose theme shows a cart count in the header, that is the single biggest source of load. The usual answers are to load fragments only on pages where the basket is actually shown, or to replace the live count with one rendered in JavaScript from the cookie. WordPress speed and caching covers the rest of the stack.
The database, and the tables that grow#
WooCommerce is a database application wearing a website. Four things there decide how it ages.
Autoloaded options. Every request loads every row in wp_options marked to autoload, into PHP memory, before anything else happens. Plugins dump settings, licence blobs and cached API responses there and never clean up. A shop with 3 MB of autoloaded options is paying that cost on every single request, including the AJAX ones.
SELECT option_name, LENGTH(option_value) AS bytesFROM wp_optionsWHERE autoload IN ('yes', 'on', 'auto', 'auto-on')ORDER BY bytes DESCLIMIT 20;Anything over about 100 KB in that list deserves an explanation. Expired transients - rows whose names start _transient_ - are safe to delete, and WP-CLI does it in one command with wp transient delete --expired.
Order storage. WooCommerce used to store orders as posts in wp_posts with their fields in wp_postmeta, which meant a simple order search was a pile of self-joins. High-Performance Order Storage replaced that with dedicated tables - wp_wc_orders, wp_wc_orders_meta, wp_wc_order_addresses and wp_wc_order_operational_data. It has been the default for new installations since WooCommerce 8.2; older shops opt in under WooCommerce, Settings, Advanced, Features, which runs a synchronisation first. If your shop predates that and admin order screens crawl, this is the fix, and it is a bigger win than any amount of extra RAM.
Action Scheduler. WooCommerce queues background work - emails, stock syncs, subscription renewals, analytics imports - in wp_actionscheduler_actions. Completed actions are retained for about a month and then purged by a cleanup job. That cleanup is itself a scheduled action, so on a shop where cron does not run properly the table grows without limit, and shops with millions of rows in it are a common sight. Check the count under WooCommerce, Status, Scheduled Actions before assuming the database is fine.
Lookup tables. wp_wc_product_meta_lookup, wp_wc_customer_lookup and the analytics tables exist so that filtering and reporting do not have to read wp_postmeta. They are rebuilt from the Status, Tools screen if they drift, which is worth knowing when reports show numbers that do not match the orders list.
One practical note on the engine: WordPress and WooCommerce are written for MySQL-compatible databases and cannot use anything else. On RE:NODE, web plans include two database slots created in the panel, with a generated host, user and password and an Open in phpMyAdmin button that signs you in with a token valid for 60 seconds. The separately sold database hosting line is PostgreSQL and MongoDB, which is the right tool for plenty of things and is not what a WordPress shop connects to.
Cron: wp-cron is not cron#
WordPress does not have a scheduler. It has a check that runs on page loads, and if something is due it runs on that visitor's request. On a shop with steady traffic that mostly works. On a shop with quiet nights, it means scheduled emails, stock syncs, subscription renewals and the Action Scheduler cleanup all wait for somebody to visit - and on a shop with heavy traffic it means every hundredth visitor pays for your background jobs.
Replace it with a real cron entry:
define( 'DISABLE_WP_CRON', true );# Every five minutes, from the system crontab*/5 * * * * curl -sS https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1# Better, if WP-CLI is available: no HTTP round trip, real exit codes*/5 * * * * cd /var/www/html && wp cron event run --due-now --quietFive minutes is the right interval for most shops. One minute is defensible if you run subscriptions or live stock sync; anything shorter is just load. If you are new to the syntax, cron expressions explained takes the five fields apart, and scheduled tasks worth having lists the ones that earn their place.
Sessions, object caching and cart fragments#
Every shopper with a basket gets a row in wp_woocommerce_sessions, keyed by a cookie, cleaned up by a daily scheduled action. On a shop where cron is broken, this table also grows without bound, and because it is read and written on nearly every uncached request, a bloated sessions table is felt everywhere.
A persistent object cache is the single biggest improvement available to a busy shop. Without one, WordPress rebuilds its internal caches on every request and transients are read from and written to the database. With one, options, transients and query results live in memory across requests, and the database load drops sharply - often by half on a shop with a large catalogue. It needs a Redis or Memcached service plus the matching object-cache.php drop-in.
Be clear about what that requires: it is a separate service, not a setting. RE:NODE does not sell a managed Redis or Memcached, so on a shared web plan there is nothing to point the drop-in at. On a VDS you install and run whichever you prefer alongside the site. Redis, when you need it is the honest version of when it is worth the extra moving part - and for a shop under twenty orders a day, it usually is not.
Media, backups and the data you cannot recreate#
The files on disk are replaceable. Product images can be re-uploaded, plugins reinstalled, the theme pulled from Git. Orders and customers cannot be recreated from anything, which makes the database the part that deserves the paranoia.
- Back up before every update. Plugin updates on a shop are a change to production. Take a backup you could restore in five minutes first, every time.
- Keep backups off the machine. A backup on the same disk protects against your mistakes, not against the disk. On RE:NODE, backup slots are included on every plan, stored off the machine they protect, restorable with a button, and lockable so rotation does not eat the one you care about. Deleting a server deletes its backups, locked ones included.
- Restore one occasionally. Database backups and restores and testing a restore before you need it both exist because a backup nobody has restored is a hypothesis.
- Do not store card data. Use a gateway that keeps card details off your server entirely - a hosted field or a redirect. This removes almost the entire PCI burden from your hosting and is the correct choice for every shop under significant scale.
Measuring instead of guessing#
Before upgrading a plan, find out which resource is actually at its limit. The panel's graphs show memory, CPU and disk against the limits; reading a server load graph covers what the shapes mean. Inside WordPress, three tools answer nearly everything:
- Query Monitor shows every query on a page, how long each took, and which plugin fired it. Install it on staging, reproduce the slow page, and you usually have your answer in one look.
- WooCommerce, Status reports the PHP and database versions, the memory limit, the extensions, and whether cron and scheduled actions are healthy.
- The slow query log, if you can reach the database server's configuration, tells you which query is actually costing you rather than which one you suspect.
A shop on the right plan should serve a cached category page in well under 200 ms of server time and an uncached product page in under 500 ms. If cached pages are slow, the problem is the server. If only uncached pages are slow, the problem is PHP, the plugins, or the database - and more RAM will not touch it.
FAQ#
How much RAM does a WooCommerce shop need?
2 GB for a small shop under about twenty orders a day, 4 GB once traffic is steady, and 8 GB for a few hundred orders a day or a catalogue in the tens of thousands. Memory is consumed by PHP workers, so the honest question is how many simultaneous uncacheable requests you need to serve. How much RAM does WordPress need has the general version.
Can I use a normal WordPress plan for WooCommerce?
Yes, if it has the memory and the PHP settings above. The difference is not the software, it is that a shop cannot serve its most important pages from cache, so the same plan supports far fewer concurrent shoppers than it does readers. Budget roughly double what a content site of the same traffic would need.
Does WooCommerce need a dedicated database server?
Not until it is large. Two or three hundred orders a day runs fine with the database on the same machine. A separate database server helps when you have several application servers, or when the database working set no longer fits in memory - and it adds a network round trip to every query, so it is not a free upgrade.
Why is my shop slow only in the admin?
Almost always the orders or products list, and almost always a legacy install still storing orders as posts. Switch on High-Performance Order Storage, check the size of the Action Scheduler table, and look at autoloaded options. All three are database problems that no amount of front-end caching touches.
Do I need Redis for WooCommerce?
Only once the database is the bottleneck, which for most shops is later than the advice suggests. A persistent object cache is a large win on a busy shop with a big catalogue and does very little on a small one. It also needs a Redis or Memcached service to exist, which a shared web plan here does not provide.
Will hosting fix my Core Web Vitals?
Partly. Hosting owns time-to-first-byte, and a slow TTFB caps everything downstream. Largest Contentful Paint and layout shift are mostly theme, image sizes and third-party scripts, and moving to a bigger plan changes none of them.




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.