Optimization

Boosting WordPress PageSpeed on an AlmaLinux VPS in Vietnam

Your WordPress site on an AlmaLinux VPS in Vietnam loads in 3 seconds while the same content on a static page loads in 300 milliseconds. That gap is almost never the theme. It is PHP workers, database queries and cache headers. This guide tunes a WordPress stack on AlmaLinux 9 so the server hands off pages fast, then leaves the rest to the browser. Every command runs on a fresh AlmaLinux 9 VPS with root access or a sudo-capable user.

  • Set PHP-FPM worker pools sized to real RAM, not to defaults.
  • Enable OPcache so WordPress never recompiles the same PHP files.
  • Add an Nginx fastcgi cache so signed-out visitors skip PHP entirely.
  • Trim MariaDB so it stops swapping on a 4GB VPS.

在越南 VPS 上,WordPress 性能的关键是 Nginx 缓存加 PHP-FPM 调优,而不是换主题。

On a Vietnam VPS, WordPress performance comes from Nginx caching plus PHP-FPM tuning, not from swapping the theme.

Why WordPress on an AlmaLinux VPS Needs Tuning

WordPress was written for shared hosting, where one request per second was normal. A VPS does not fix that by itself. The default PHP-FPM pool on AlmaLinux ships a small number of workers, OPcache is disabled unless your PHP package enables it, and WordPress runs its full bootstrap on every uncached request. On a site with 30 plugins, that bootstrap touches dozens of files before a single byte of HTML goes out.

PageSpeed scores from Google split into two parts: field data (real users) and lab data (a single test run). Both weight server response time heavily. Reducing Time to First Byte (TTFB) from 1.2 seconds to 200 milliseconds moves the needle more than any image tweak. Serve from a Vietnam IPv4 in the same country as your audience and the network round trip stays under 30 milliseconds for most of the country, so the tuning you do below is what shows up in the score.

Step 1 - Confirm Your AlmaLinux and PHP Stack

Check what you actually have before changing anything. AlmaLinux 9 ships PHP 8.0, but the Remi repository gives you PHP 8.3, which WordPress and most modern plugins target.

cat /etc/almalinux-release
php -v
nginx -v
mysql --version

If PHP shows 8.0, install 8.3 from Remi:

dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
dnf module reset php -y
dnf module enable php:remi-8.3 -y
dnf update -y php\*
systemctl restart php-fpm nginx

Verify: php -v should print PHP 8.3.x. Then confirm the FPM service is running with systemctl status php-fpm and expect active (running).

Step 2 - Tune PHP-FPM Worker Pools

The default pm = dynamic pool with pm.max_children = 5 chokes under any concurrent load. The right number is memory-bound: each PHP worker under WordPress uses roughly 40-60MB. On a 4GB VPS with MariaDB and Nginx taking about 1.5GB, you have about 2GB left for PHP, so 30 to 35 workers is the ceiling.

Edit the pool config. Open /etc/php-fpm.d/www.conf and set:

pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 6
pm.max_spare_servers = 12
pm.max_requests = 500
request_terminate_timeout = 120s

pm.max_requests = 500 is a small hedge against memory leaks in plugins. It recycles a worker after 500 requests so a slow leak never takes the pool down.

Also set the PHP limits WordPress needs. In /etc/php.ini:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.validate_timestamps = 0
opcache.revalidate_freq = 0

The last two lines matter: setting validate_timestamps = 0 tells OPcache to never check if a file changed. You get the speed, but you must reload FPM after any PHP file change:

systemctl reload php-fpm

Verify: run php-fpm -t for a syntax check, then systemctl status php-fpm. Open phpinfo() and confirm opcache.enable is On.

Step 3 - Add the Nginx fastcgi Cache

This is the single biggest win. An Nginx fastcgi cache stores the generated HTML on disk and serves it in microseconds without waking PHP. WordPress shipped wp-cache.php for years but never enabled the fastcgi cache backend, so you enable it manually.

In /etc/nginx/nginx.conf, inside the http block, add:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=2g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_lock on;
fastcgi_cache_background_update on;

Then in your site's server block, add the bypass rules. Skip the cache for logged-in users, the admin area, and anything with a WordPress session cookie:

set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php|wp-.*.php|/feed/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { set $skip_cache 1; }

location ~ \.php$ {
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 301 302 60m;
    fastcgi_pass unix:/run/php-fpm/www.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Create the cache directory and fix ownership so Nginx can write:

mkdir -p /var/cache/nginx
chown nginx:nginx /var/cache/nginx
nginx -t
systemctl reload nginx

Verify: curl the homepage twice and check for the header:

curl -I https://yourdomain.com/ | grep -i x-fastcgi-cache

The first request returns MISS, the second returns HIT. Add add_header X-FastCGI-Cache $upstream_cache_status; if the header does not appear.

Step 4 - Trim MariaDB So It Stops Swapping

MariaDB defaults to a 128MB InnoDB buffer pool, which is tiny, and a query cache that MariaDB removed in 10.6+. On a 4GB VPS, set the buffer pool to about 40-50% of RAM, but watch the shared server. If MariaDB starts eating into PHP workers you will see queries pile up.

Edit /etc/my.cnf.d/mariadb-server.cnf:

[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
max_connections = 80
tmp_table_size = 64M
max_heap_table_size = 64M

Restart and verify swap is idle:

systemctl restart mariadb
free -m
mysqladmin status

If swap used stays under 100MB after a busy hour, you are sized correctly. If it climbs, cut innodb_buffer_pool_size to 768M and add swap with the guidance in our low-RAM memory optimization guide.

Step 5 - Reduce WordPress Bloat and Add a Cache Plugin

Nginx handles page caching, but WordPress still needs help with database queries and wp-cron. Add a lightweight object cache and disable the default cron runner:

wp plugin install redis-cache --activate
wp config set WP_REDIS_HOST 127.0.0.1
wp config set DISABLE_WP_CRON true
wp redis enable

Then move wp-cron to a systemd timer or a real cron job:

*/5 * * * * curl -s https://yourdomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

Trim plugins next. Audit active plugins with wp plugin list --status=active. Pages builders and backup plugins that run on every request are the usual TTFB killers. If your PHP worker pool is constantly maxed, our RAM troubleshooting guide shows how to spot the culprit with systemctl and ps.

Verify: run wp cache flush and load the site. Redis should report Connected in the wp-admin dashboard widget.

Step 6 - Measure and Iterate

Do not guess. Run a scoped benchmark before and after each change. The command below fires 20 requests and reports the TTFB:

for i in {1..20}; do curl -so /dev/null -w "%{time_starttransfer}\n" https://yourdomain.com/; done | awk '{sum+=$1} END {print "avg TTFB: " sum/NR "s"}'

On a Vietnam VPS serving Vietnam users, a tuned stack with the fastcgi cache in front of it lands well under 0.2 seconds for cached pages. Uncached pages depend on your plugins. If TTFB stays north of 0.5 seconds after caching, look at the database, not the theme.

Pair this with a real NVMe VPS hosting Vietnam setup: NVMe storage removes disk I/O from the cache read path, which matters when you are serving thousands of small static files. If you are still on a 2GB instance and watching memory pressure, our WordPress VPS RAM sizing guide covers when it is time to step up.

Troubleshooting Common Issues

Nginx returns 502 after PHP-FPM tuning. Check the PHP-FPM log: journalctl -u php-fpm --since "10 min ago". A pool that cannot fork a worker logs "unable to fork" and the fix is lowering pm.max_children or raising nproc limits.

Cache HIT on admin pages. Your skip rules are wrong. Add if ($request_uri ~* "/wp-admin/") { set $skip_cache 1; } and reload Nginx with nginx -t && systemctl reload nginx. Never let a logged-in user see a cached page.

OPcache changes do not show up. Because opcache.validate_timestamps = 0 is set, PHP will keep serving compiled versions until you run systemctl reload php-fpm. Get in the habit of reloading FPM after every deploy.

FAQ

How much RAM does WordPress need on an AlmaLinux VPS?

4GB is the practical floor for a site with caching and a handful of plugins. 2GB works only if you run a lean plugin set and keep PHP workers low. If swap usage climbs above a few hundred megabytes under normal traffic, step up a plan.

Does the Nginx fastcgi cache replace a WordPress cache plugin?

No. Nginx caches the full HTML page, but a plugin like Redis Object Cache still handles database queries for logged-in users. Use both: Nginx for anonymous visitors, Redis for authenticated sessions.

Why is my page still slow after enabling the cache?

Check the response header first. If it says BYPASS or MISS on a second request, your skip rules are matching. A stray cookie or a query string is the usual cause. Test with a clean browser profile.

Should I disable OPcache timestamp validation?

Yes, in production. It removes a stat call per request. The trade-off is that you must reload PHP-FPM after every deploy. On a dev server, leave it enabled to avoid that step.

Is a Vietnam VPS better than a Singapore VPS for Vietnamese users?

For users inside Vietnam, a domestic VPS keeps the network path short and predictable. A Singapore host adds international transit that varies by carrier. Read our comparison of Vietnam and Singapore VPS performance before deciding.

Related articles

AlmaLinux VPS 上 WordPress 性能优化要点

在越南 AlmaLinux 9 VPS 上提升 WordPress 的 PageSpeed,关键不是换主题,而是调整 PHP-FPM 进程池、启用 OPcache、配置 Nginx fastcgi 缓存并优化 MariaDB。缓存让匿名访客直接拿到静态 HTML,无需唤醒 PHP 进程。建议先用 curl 测量 TTFB,再逐项改配置并观察内存与 swap 占用。启用 NVMe 存储的越南 VPS 能进一步减少缓存读取的磁盘延迟。

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.