{"id":3283,"date":"2026-09-29T10:39:13","date_gmt":"2026-09-29T10:39:13","guid":{"rendered":"https:\/\/siteharbour.com\/blog\/?p=3283"},"modified":"2026-09-30T10:05:39","modified_gmt":"2026-09-30T10:05:39","slug":"optimize-wordpress-1gb-ram-server","status":"publish","type":"post","link":"https:\/\/siteharbour.com\/blog\/optimize-wordpress-1gb-ram-server\/","title":{"rendered":"How to Optimize WordPress on a 1 GB RAM Server"},"content":{"rendered":"<p>Yes, WordPress can run well on a server with 1 GB of RAM, as long as you tune the whole stack instead of a single setting, and make sure most visitors are served from a page cache rather than by PHP. The changes below are ordered by impact: cache pages, cap PHP-FPM workers, size MariaDB to your real data, enable OPcache, trim plugins, and add swap as a safety net.<\/p>\n<p>The examples use Ubuntu 24.04 with Nginx, PHP 8.3 and MariaDB (MySQL works the same way; only paths and service names differ). Replace <code>8.3<\/code> with your PHP version and adjust paths for your distribution. Every step starts with measurement, because the right numbers depend on your plugins, theme and traffic, not on a universal template.<\/p>\n<h2>Where the memory actually goes<\/h2>\n<p>On a 1 GB server, RAM is shared by the operating system, Nginx, PHP-FPM workers, MariaDB and anything else you run, such as a mail daemon, a monitoring agent or a second site. Two components decide whether the server stays stable:<\/p>\n<ul>\n<li><strong>PHP-FPM workers.<\/strong> Each concurrent PHP request needs its own worker process. A lean WordPress worker can use tens of megabytes; a site with a page builder, WooCommerce or many plugins can use well over 100 MB per worker. Total PHP memory is the number of workers multiplied by their average size.<\/li>\n<li><strong>MariaDB or MySQL.<\/strong> Memory is dominated by the InnoDB buffer pool plus per-connection buffers. Default settings are often sized for much larger machines.<\/li>\n<\/ul>\n<p>As a rough planning sketch, not a benchmark: reserve roughly 150 to 250 MB for the operating system and background services, 30 to 50 MB for Nginx, and 150 to 250 MB for MariaDB. That leaves somewhere around 350 to 500 MB for PHP-FPM. Your real figures will differ, which is why the first step is to measure.<\/p>\n<h2>Step 1: Measure before you change anything<\/h2>\n<p>Start with a snapshot of overall memory and the biggest consumers:<\/p>\n<pre><code>free -h\nps -eo pid,rss,cmd --sort=-rss | head -15<\/code><\/pre>\n<p>The <code>rss<\/code> column is in kilobytes. To see how large your PHP-FPM workers really are, average them:<\/p>\n<pre><code>ps -C php-fpm8.3 -o rss= | awk '{s+=$1; n++} END {printf \"processes: %d, average: %.0f MB\\n\", n, s\/n\/1024}'<\/code><\/pre>\n<p>This includes the small master process and counts shared memory more than once, so treat it as an upper estimate. For a fairer figure, install <code>smem<\/code> and read the proportional set size (PSS) column:<\/p>\n<pre><code>sudo apt install smem\nsudo smem -t -k -P php-fpm<\/code><\/pre>\n<p>Then check whether the server is already under memory pressure. Watch the <code>si<\/code> and <code>so<\/code> columns of <code>vmstat<\/code>; values that stay above zero mean the system is actively swapping. Also look for out-of-memory kills:<\/p>\n<pre><code>vmstat 1 5\nsudo journalctl -k | grep -i \"out of memory\"<\/code><\/pre>\n<p>Write these numbers down. They are your baseline for judging every change that follows.<\/p>\n<h2>Step 2: Serve pages from a cache so PHP barely runs<\/h2>\n<p>The single biggest improvement on a small server is not running PHP for anonymous visitors at all. Nginx FastCGI caching stores the finished HTML and serves it directly, so most requests never reach WordPress or the database.<\/p>\n<p>First define the cache zone in the <code>http<\/code> context, for example in <code>\/etc\/nginx\/conf.d\/wp-cache.conf<\/code>:<\/p>\n<pre><code>fastcgi_cache_path \/var\/cache\/nginx\/wordpress levels=1:2 keys_zone=WPCACHE:10m max_size=256m inactive=60m use_temp_path=off;\nfastcgi_cache_key \"$scheme$request_method$host$request_uri\";<\/code><\/pre>\n<p>Then, in your site&#8217;s <code>server<\/code> block, decide which requests must never be cached, and attach the cache to the PHP location:<\/p>\n<pre><code>set $skip_cache 0;\nif ($request_method = POST) { set $skip_cache 1; }\nif ($query_string != \"\") { set $skip_cache 1; }\nif ($request_uri ~* \"\/wp-admin\/|\/wp-login.php|\/xmlrpc.php|wp-.*\\.php|\/feed\/\") { set $skip_cache 1; }\nif ($http_cookie ~* \"comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in\") { set $skip_cache 1; }\n\nlocation ~ \\.php$ {\n    include snippets\/fastcgi-php.conf;\n    fastcgi_pass unix:\/run\/php\/php8.3-fpm.sock;\n    fastcgi_cache WPCACHE;\n    fastcgi_cache_valid 200 301 302 30m;\n    fastcgi_cache_bypass $skip_cache;\n    fastcgi_no_cache $skip_cache;\n    fastcgi_cache_lock on;\n    fastcgi_cache_use_stale error timeout updating http_500 http_503;\n    add_header X-FastCGI-Cache $upstream_cache_status;\n}<\/code><\/pre>\n<p>Create the directory, test the configuration and reload:<\/p>\n<pre><code>sudo mkdir -p \/var\/cache\/nginx\/wordpress\nsudo chown www-data:www-data \/var\/cache\/nginx\/wordpress\nsudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/code><\/pre>\n<p>Verify it works by requesting the same page twice. The first response should say <code>MISS<\/code> and the second <code>HIT<\/code>:<\/p>\n<pre><code>curl -sI https:\/\/example.com\/ | grep -i x-fastcgi-cache\ncurl -sI https:\/\/example.com\/ | grep -i x-fastcgi-cache<\/code><\/pre>\n<p>Two things to know. Requests with a query string bypass the cache in this setup, so links with tracking parameters are served dynamically. And new or edited content appears when the cached copy expires, or when you clear the cache directory. If you always see <code>BYPASS<\/code> or <code>MISS<\/code>, look for a plugin that sends a <code>Set-Cookie<\/code> or <code>Cache-Control: no-cache<\/code> header on public pages. If you cannot change the Nginx configuration, a lightweight page-cache plugin is the fallback, but the server-level cache uses less memory.<\/p>\n<h2>Step 3: Right-size PHP-FPM<\/h2>\n<p>The pool configuration decides how many PHP workers can exist at once. Too many and the server runs out of memory; too few and requests queue. The three process managers behave differently:<\/p>\n<table>\n<thead>\n<tr>\n<th>Mode<\/th>\n<th>Behaviour<\/th>\n<th>Best for<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>static<\/code><\/td>\n<td>A fixed number of workers is always running<\/td>\n<td>Busy, predictable servers with plenty of RAM<\/td>\n<\/tr>\n<tr>\n<td><code>dynamic<\/code><\/td>\n<td>Keeps a pool between minimum and maximum spare workers<\/td>\n<td>Steady traffic where consistent latency matters<\/td>\n<\/tr>\n<tr>\n<td><code>ondemand<\/code><\/td>\n<td>Starts workers when requests arrive and stops idle ones<\/td>\n<td>Small servers with low or bursty traffic<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>On a 1 GB server with a page cache in front, <code>ondemand<\/code> is usually a sensible starting point because idle workers use no memory. The trade-off is a small delay when a worker has to start after a quiet period.<\/p>\n<p>To choose <code>pm.max_children<\/code>, divide the memory you can give PHP by the average worker size you measured. As a worked example only: if PHP can use about 400 MB and workers average 55 MB, then 400 \u00f7 55 is about 7, so starting at 6 leaves headroom. Edit <code>\/etc\/php\/8.3\/fpm\/pool.d\/www.conf<\/code>:<\/p>\n<pre><code>pm = ondemand\npm.max_children = 6\npm.process_idle_timeout = 20s\npm.max_requests = 500<\/code><\/pre>\n<p><code>pm.max_requests<\/code> recycles each worker after that many requests, which contains slow memory growth from leaky code. Keep <code>memory_limit<\/code> in mind as well: it is a per-request ceiling, not the typical size of a worker, so a value such as 128M is a reasonable default, and only raise it if a specific admin task needs more.<\/p>\n<p>Test and reload, then watch the PHP-FPM log for the warning that means your limit is too low:<\/p>\n<pre><code>sudo php-fpm8.3 -t &amp;&amp; sudo systemctl reload php8.3-fpm\nsudo grep -i \"max_children\" \/var\/log\/php8.3-fpm.log<\/code><\/pre>\n<p>If you see &#8220;server reached pm.max_children&#8221; repeatedly during normal traffic, the fix is a faster site or more RAM, not blindly raising the number until the server starts swapping.<\/p>\n<p>For a full walkthrough of measuring worker size and calculating this limit, see <a href=\"\/blog\/optimize-php-fpm-workers-limited-ram\/\">how to optimize PHP-FPM workers for limited RAM<\/a>.<\/p>\n<h2>Step 4: Enable and size OPcache<\/h2>\n<p>OPcache keeps compiled PHP scripts in shared memory so WordPress does not recompile its files on every request. It saves CPU and speeds up every uncached page. On a small server, a modest allocation is enough. In <code>\/etc\/php\/8.3\/fpm\/conf.d\/10-opcache.ini<\/code>:<\/p>\n<pre><code>opcache.enable=1\nopcache.memory_consumption=96\nopcache.interned_strings_buffer=8\nopcache.max_accelerated_files=10000\nopcache.validate_timestamps=1\nopcache.revalidate_freq=60<\/code><\/pre>\n<p>With <code>validate_timestamps=0<\/code>, PHP never checks whether files changed. That is faster, but it means you must reload PHP-FPM after every plugin, theme or core update, or the old code keeps running. If you are not automating that reload, leave timestamp validation on.<\/p>\n<h2>Step 5: Cap MariaDB or MySQL memory<\/h2>\n<p>First check how big your WordPress data really is, because the buffer pool only needs to hold the data you actually use. Replace <code>wordpress<\/code> with your database name:<\/p>\n<pre><code>sudo mysql -e \"SELECT ROUND(SUM(data_length+index_length)\/1024\/1024,1) AS size_mb FROM information_schema.tables WHERE table_schema='wordpress';\"<\/code><\/pre>\n<p>Instead of editing the packaged file, create your own override in <code>\/etc\/mysql\/mariadb.conf.d\/99-lowram.cnf<\/code> (MariaDB) or <code>\/etc\/mysql\/mysql.conf.d\/zz-lowram.cnf<\/code> (MySQL; the name makes it load after the packaged <code>mysqld.cnf<\/code>) so package updates do not overwrite it:<\/p>\n<pre><code>[mysqld]\ninnodb_buffer_pool_size = 128M\nmax_connections = 40\ntmp_table_size = 32M\nmax_heap_table_size = 32M<\/code><\/pre>\n<p>For many small WordPress sites, 128 to 256 MB is plenty for the buffer pool; go higher only if the database is larger and you have measured memory to spare. Forty connections is generous when PHP is limited to a handful of workers. Restart the service and confirm the values took effect:<\/p>\n<pre><code>sudo systemctl restart mariadb   # on MySQL: sudo systemctl restart mysql\nsudo mysql -e \"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';\"\nps -C mariadbd,mysqld -o comm=,rss=<\/code><\/pre>\n<p>Leave durability settings such as <code>innodb_flush_log_at_trx_commit<\/code> alone. Trading safety for a little speed is not worth it on a production site.<\/p>\n<h2>Step 6: Small Nginx wins for static files<\/h2>\n<p>Nginx itself uses little memory, but letting browsers cache static files reduces the number of requests your server handles at all. Add compression and long expiry headers for assets:<\/p>\n<pre><code>gzip on;\ngzip_types text\/css application\/javascript application\/json image\/svg+xml;\n\nlocation ~* \\.(?:css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2)$ {\n    expires 30d;\n    add_header Cache-Control \"public\";\n    access_log off;\n}<\/code><\/pre>\n<p>Also compress and resize images before uploading them. Serving a 3 MB photo that displays at 800 pixels wide wastes bandwidth and slows every page that uses it.<\/p>\n<h2>Step 7: Find and remove plugin overhead<\/h2>\n<p>Plugins are the most common reason a WordPress worker grows large. Start by listing what is active and removing anything you do not use:<\/p>\n<pre><code>wp plugin list --status=active<\/code><\/pre>\n<p>To see where time and database queries go, install WP-CLI&#8217;s profiling package and inspect the stages of a page load:<\/p>\n<pre><code>wp package install wp-cli\/profile-command\nwp profile stage --spotlight<\/code><\/pre>\n<p>The result shows whether the cost sits in bootstrap (plugins loading), the main query, or the template. For memory, a practical method is to deactivate a suspect plugin on a staging copy, restart PHP-FPM, warm the site with some requests and compare the worker size again. Be cautious with all-in-one security suites, backup plugins that run inside WordPress, page builders and anything that runs scheduled scans; on a small server, server-level backups and a firewall usually do the same job more cheaply.<\/p>\n<h2>Step 8: WP-Cron and autoloaded options<\/h2>\n<p>By default, WordPress runs scheduled tasks when visitors load pages. On a cached site that is unreliable, and a slow task can tie up a worker. Move it to a real system job. In <code>wp-config.php<\/code> add:<\/p>\n<pre><code>define('DISABLE_WP_CRON', true);<\/code><\/pre>\n<p>Then run it from the web user&#8217;s crontab (<code>sudo crontab -u www-data -e<\/code>), adjusting the path:<\/p>\n<pre><code>*\/5 * * * * cd \/var\/www\/example &amp;&amp; \/usr\/bin\/php wp-cron.php &gt;\/dev\/null 2&gt;&amp;1<\/code><\/pre>\n<p>Next, check autoloaded options. WordPress loads every option flagged for autoload on every single request, so a bloated set slows every uncached page and inflates memory. Site Health starts warning at roughly 800 KB. Check the total, and list the largest entries (replace <code>wp_<\/code> with your table prefix):<\/p>\n<pre><code>wp db query \"SELECT ROUND(SUM(LENGTH(option_value))\/1024) AS autoload_kb FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto');\"\nwp db query \"SELECT option_name, LENGTH(option_value) AS bytes FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto') ORDER BY bytes DESC LIMIT 10;\"<\/code><\/pre>\n<p>Large entries often belong to plugins you removed long ago. Research an option before changing it, and take a database backup first. Also clear expired transients periodically and cap stored revisions:<\/p>\n<pre><code>wp transient delete --expired\n# in wp-config.php\ndefine('WP_POST_REVISIONS', 5);<\/code><\/pre>\n<h2>Step 9: Add swap as a safety net, not as extra RAM<\/h2>\n<p>Swap will not make a struggling server fast, but a small swap file can turn a sudden memory spike into a brief slowdown instead of an out-of-memory kill. Create a 1 GB file and keep the system reluctant to use it:<\/p>\n<pre><code>sudo fallocate -l 1G \/swapfile\nsudo chmod 600 \/swapfile\nsudo mkswap \/swapfile\nsudo swapon \/swapfile\necho '\/swapfile none swap sw 0 0' | sudo tee -a \/etc\/fstab\necho 'vm.swappiness=10' | sudo tee \/etc\/sysctl.d\/99-swappiness.conf\nsudo sysctl --system\nswapon --show<\/code><\/pre>\n<p>Swap on network-backed cloud disks is slow, so it should be used rarely. If <code>vmstat<\/code> shows constant swap activity under ordinary traffic, the swap file is hiding a real capacity problem.<\/p>\n<h2>Step 10: Make failures recoverable<\/h2>\n<p>Even a well-tuned small server should restart its services automatically if one is killed. Add a systemd override for PHP-FPM:<\/p>\n<pre><code>sudo systemctl edit php8.3-fpm<\/code><\/pre>\n<p>and enter:<\/p>\n<pre><code>[Service]\nRestart=on-failure\nRestartSec=5s<\/code><\/pre>\n<p>Combine this with an external uptime monitor so you learn about downtime from an alert instead of from a visitor, and re-run the measurements from Step 1 after each change so you can see what actually helped.<\/p>\n<h2>What this looked like on a real 1 GB server<\/h2>\n<p>Numbers make Step 1 concrete, so here is a real snapshot. It comes from a production AWS EC2 server running Ubuntu 24.04 with 911 MiB of usable RAM and 2 GB of swap, with Nginx, PHP 8.4-FPM and MySQL 8.0. The same Nginx and PHP-FPM pool serve a Laravel application and a WordPress blog. It is one server at one quiet moment, so treat it as an example of what to look for, not as numbers to copy.<\/p>\n<pre><code>$ free -mh\n               total        used        free      shared  buff\/cache   available\nMem:           911Mi       664Mi       102Mi        71Mi       386Mi       247Mi\nSwap:          2.0Gi        71Mi       1.9Gi<\/code><\/pre>\n<p>Only 102 MiB is &#8220;free&#8221;, which looks alarming, but 386 MiB is cache the kernel hands back when programs need it. The <code>available<\/code> column, 247 MiB, is the honest measure of headroom. About 71 MiB has been moved to swap, so the next question is whether the server is swapping right now. <code>vmstat<\/code> answers it, with samples taken one second apart:<\/p>\n<pre><code>$ vmstat 1 5\nprocs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------\n r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu\n 0  0  74260 139220   9288 370080    0    0     0     0  385  616  0  1 99  0  0  0\n 0  0  74260 142420   9320 370080    0    0     0   120  588  987  1  1 98  1  0  0<\/code><\/pre>\n<p>The <code>si<\/code> and <code>so<\/code> columns (swap-in and swap-out) are zero, so those 71 MiB are old pages parked in swap, not active swapping. The CPU was about 99 percent idle at the time.<\/p>\n<p>The biggest memory users, from <code>ps -eo pid,rss,cmd --sort=-rss<\/code>, in resident memory (RSS), rounded to megabytes:<\/p>\n<table>\n<thead>\n<tr>\n<th>Process<\/th>\n<th>Resident memory (RSS)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Database server (<code>mysqld<\/code>)<\/td>\n<td>about 188 MB<\/td>\n<\/tr>\n<tr>\n<td>PHP-FPM workers (three running)<\/td>\n<td>92 to 98 MB each<\/td>\n<\/tr>\n<tr>\n<td>PHP-FPM master process<\/td>\n<td>about 40 MB<\/td>\n<\/tr>\n<tr>\n<td>fail2ban<\/td>\n<td>about 34 MB<\/td>\n<\/tr>\n<tr>\n<td>multipathd<\/td>\n<td>about 25 MB<\/td>\n<\/tr>\n<tr>\n<td>snapd<\/td>\n<td>about 15 MB<\/td>\n<\/tr>\n<tr>\n<td>fwupd<\/td>\n<td>about 12 MB<\/td>\n<\/tr>\n<tr>\n<td>amazon-ssm-agent<\/td>\n<td>about 11 MB<\/td>\n<\/tr>\n<tr>\n<td>Nginx worker<\/td>\n<td>about 10 MB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Why RSS misleads: RSS versus PSS<\/h3>\n<p>Those PHP-FPM figures look alarming, but RSS counts every shared page, such as the OPcache segment and shared libraries, in full for every worker. Proportional set size (PSS) divides each shared page between the processes that use it, so it is the fairer number. You can read it without installing anything:<\/p>\n<pre><code>for p in $(pgrep -f 'php-fpm: pool www'); do echo -n \"$p \"; sudo grep '^Pss:' \/proc\/$p\/smaps_rollup; done<\/code><\/pre>\n<p>On this server it printed:<\/p>\n<pre><code>1537590 Pss:               31918 kB\n1537591 Pss:               35464 kB\n1537595 Pss:               33173 kB<\/code><\/pre>\n<p>That is about 31 to 35 MB per worker, roughly a third of what RSS suggested, for the same processes at the same moment. The whole difference is shared memory being counted more than once.<\/p>\n<p>This changes the sizing arithmetic. Suppose you can give PHP 300 MB. Dividing by RSS gives about 3 workers (300 \u00f7 95); dividing by PSS gives about 9 (300 \u00f7 33). PSS is closer to the truth, but it is still a snapshot: workers grow while handling heavy requests, so leave a margin and check under real load. This pool is configured with:<\/p>\n<pre><code>pm = dynamic\npm.max_children = 5\npm.start_servers = 2\npm.min_spare_servers = 1\npm.max_spare_servers = 3\npm.max_requests = 500<\/code><\/pre>\n<p>Five workers at the measured PSS come to about 165 MB, which is comfortable here. A cap like this is also useful for protecting the rest of the machine, because it stops a burst of slow requests from starting more workers than the server can afford.<\/p>\n<h3>What else the snapshot shows<\/h3>\n<ul>\n<li><strong>The database is the largest single process,<\/strong> at roughly a fifth of all RAM in RSS terms.<\/li>\n<li><strong>A stock cloud image runs services you may not need.<\/strong> multipathd, snapd and fwupd use roughly 50 MB together (RSS, so an upper bound). fail2ban is worth its memory, and the SSM agent is required if you use AWS Systems Manager, so check what depends on a service before disabling it.<\/li>\n<\/ul>\n<p>Your numbers will differ, which is why measuring, and measuring the right thing, comes before any tuning.<\/p>\n<h2>When 1 GB stops being enough<\/h2>\n<p>Tuning has limits. These signs mean the server is undersized for the workload:<\/p>\n<ul>\n<li>Swap stays in use during normal traffic, with constant <code>si<\/code> and <code>so<\/code> activity in <code>vmstat<\/code>.<\/li>\n<li>The kernel log shows out-of-memory kills of MariaDB or PHP-FPM.<\/li>\n<li>The <code>pm.max_children<\/code> warning keeps returning even after the site has been slimmed down.<\/li>\n<li>Much of your traffic cannot be cached, as with WooCommerce carts, membership sites or many logged-in users.<\/li>\n<li>You are running several sites on the same 1 GB machine.<\/li>\n<\/ul>\n<p>At that point the next step is 2 GB or more of RAM, moving the database to its own server, or moving to <a href=\"\/wordpress\">managed WordPress hosting<\/a> where the stack is already tuned. For sites with sustained high traffic, a <a href=\"\/dedicated-server\">dedicated server<\/a> removes the shared-resource limits entirely.<\/p>\n<h2>A practical order of operations<\/h2>\n<ol>\n<li>Measure memory, worker size and swap activity, and write the numbers down.<\/li>\n<li>Put an Nginx FastCGI cache in front of PHP.<\/li>\n<li>Set PHP-FPM to <code>ondemand<\/code> and calculate <code>pm.max_children<\/code> from your measured worker size.<\/li>\n<li>Confirm OPcache is enabled and decide how you handle reloads after updates.<\/li>\n<li>Cap MariaDB or MySQL with a buffer pool sized to your real data.<\/li>\n<li>Remove or replace heavy plugins.<\/li>\n<li>Move WP-Cron to a system job and trim autoloaded options.<\/li>\n<li>Add a small swap file and automatic service restarts.<\/li>\n<li>Re-measure, and repeat only where the numbers show a problem.<\/li>\n<\/ol>\n<h2>Frequently asked questions<\/h2>\n<h3>How many WordPress sites can one 1 GB server run?<\/h3>\n<p>Usually one, or two small and well-cached sites. The answer depends on your measured worker size and traffic, so use the calculation in Step 3 instead of a fixed rule.<\/p>\n<h3>Is Redis worth it on 1 GB?<\/h3>\n<p>Only if many requests miss the page cache, such as logged-in users or WooCommerce. An object cache stores database results in RAM, so cap it (for example <code>maxmemory 64mb<\/code> with <code>maxmemory-policy allkeys-lru<\/code>) and re-measure. For a mostly public blog, the page cache already does most of the work.<\/p>\n<h3>Should I use Nginx or Apache?<\/h3>\n<p>Nginx with PHP-FPM generally uses less memory under concurrent load than Apache running PHP inside its worker processes. Apache with the event MPM and PHP-FPM can perform comparably, so the bigger factor is how the whole stack is configured.<\/p>\n<h3>Should I choose ondemand or dynamic?<\/h3>\n<p>Choose <code>ondemand<\/code> for low or bursty traffic on a tiny server, and <code>dynamic<\/code> if traffic is steady and you prefer consistent response times over the lowest idle memory.<\/p>\n<p>A 1 GB server is a constraint, not a dead end. Measure first, cache aggressively, cap each component with numbers from your own workload, and you can run a fast, stable WordPress site on very modest hardware.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A practical, measure-first guide to running WordPress reliably on 1 GB of RAM with Nginx, PHP-FPM, MariaDB, OPcache and caching.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[22,24,21,23],"class_list":["post-3283","post","type-post","status-publish","format-standard","hentry","category-wordpress","tag-low-ram-server","tag-server-optimization","tag-wordpress-optimization","tag-wordpress-performance"],"_links":{"self":[{"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/posts\/3283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/comments?post=3283"}],"version-history":[{"count":3,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/posts\/3283\/revisions"}],"predecessor-version":[{"id":3290,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/posts\/3283\/revisions\/3290"}],"wp:attachment":[{"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/media?parent=3283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/categories?post=3283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/siteharbour.com\/blog\/wp-json\/wp\/v2\/tags?post=3283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}