Tối ưu PHP-FPM cho WordPress trên VPS

WordPress trên VPS thường chậm dần theo thời gian không chỉ vì plugin kém mà còn do cấu hình PHP-FPM mặc định quá tham lam hoặc quá yếu. Một VPS NVMe với IPv4 riêng có thể xử lý hàng trăm request mỗi giây, nhưng nếu PHP-FPM không được tinh chỉnh, bạn sẽ thấy memory spike liên tục và CPU nghẽn ở một thread riêng biệt. Bài này sẽ đi thẳng vào các thông số thực tế: pm.max_children, pm.start_servers, opcache, và cách phân bổ pool riêng cho WordPress.
Yêu cầu trước khi bắt đầu
- VPS chạy Ubuntu 24.04 LTS (hoặc Debian 12) với user sudo non-root.
- Nginx đã cài và cấu hình làm reverse proxy (hoặc chạy trực tiếp PHP).
- WordPress đã được cài đặt trên VPS.
- PHP 8.3 (hoặc 8.2) cùng PHP-FPM đã được cài đặt.
- Quyền root hoặc sudo.
Vì sao PHP-FPM mặc định chưa tối ưu cho WordPress
PHP-FPM hoạt động theo cơ chế pool, mỗi pool có một số worker process xử lý request. Cấu hình pm = dynamic mặc định (vd pm.max_children = 5) rất an toàn cho máy 1GB RAM nhưng không đủ cho site có traffic. WordPress dùng nhiều memory mỗi request (thường 40-80MB) vì load toàn bộ plugin và theme. Nếu không tinh chỉnh, process sẽ bị kill vì OOM, hoặc ngược lại nếu để pm động quá rộng, VPS sẽ bị swap đến chậm.
Bước 1, Xác định memory tối đa dành cho PHP-FPM
Trước khi đặt số children, bạn cần biết VPS có bao nhiêu RAM khả dụng. Chạy lệnh sau để lấy memory dành riêng cho PHP-FPM (không tính OS + Nginx + MySQL).
free -m
Output ví dụ:
total used free shared buff/cache available
Mem: 1955 350 800 20 805 1400
Giả sử bạn có 2GB RAM, dành 512MB cho hệ thống + Nginx + MySQL, còn lại khoảng 1.4GB cho PHP. Mỗi worker WordPress tiêu thụ trung bình 60MB. Vậy pm.max_children = floor(1400 / 60) ≈ 23. Con số này là giới hạn cứng, không vượt quá để tránh OOM.
Công thức: max_children = (RAM_cho_PHP) / (memory_per_worker)
Bước 2, Tinh chỉnh pool WordPress riêng
Mỗi site WordPress nên có pool riêng trong PHP-FPM để cách ly tài nguyên. File cấu hình pool đặt tại /etc/php/8.3/fpm/pool.d/wordpress.conf. Nếu chưa có, copy từ mẫu:
sudo cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/wordpress.conf
Sửa file với thông số phù hợp (dùng sudo nano hoặc sudo vim):
[wordpress]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm-wordpress.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = static
pm.max_children = 23
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500
Giải thích:
pm = static: luôn giữ số worker cố định, tránh dao động memory, phù hợp nếu traffic ổn định. Nếu traffic theo mùa vụ, dùngpm = dynamicnhưng cần đặt start/min/max khéo.pm.max_children = 23: dựa trên RAM đã tính ở bước 1.pm.max_requests = 500: mỗi worker xử lý 500 request rồi tự kill để tránh memory leak.- Socket riêng: không dùng chung socket
/run/php/php8.3-fpm.sockđể tránh xung đột với site khác.
Kiểm tra cấu hình:
sudo php-fpm8.3 -t
Kết quả mong đợi: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful.
Bước 3, Cập nhật Nginx để dùng socket riêng
Sửa file config site của WordPress (vd /etc/nginx/sites-available/wordpress), phần location ~ \.php$:
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm-wordpress.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
Kiểm tra Nginx và restart:
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm
Verify: Xem pool wordpress đã chạy:
ps aux | grep php-fpm | grep wordpress
Bạn sẽ thấy dòng php-fpm: pool wordpress cùng 23 process.
Bước 4, Tối ưu Opcache cho WordPress
Opcache lưu mã PHP đã biên dịch vào shared memory, giảm thời gian parse mỗi request. WordPress chạy rất nhiều file (core + plugin + theme) nên kích hoạt opcache là bước quan trọng nhất sau pool tuning.
Sửa file /etc/php/8.3/fpm/conf.d/10-opcache.ini (hoặc /etc/php/8.3/fpm/php.ini):
[opcache]
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=120
opcache.fast_shutdown=1
Giải thích:
memory_consumption=256: dành 256MB cho opcache, đủ với hầu hết WordPress có dưới 100 plugin.max_accelerated_files=10000: giới hạn số file được cache. WordPress core + plugin phổ biến tầm 3000-5000 files.revalidate_freq=120: kiểm tra file thay đổi 2 phút một lần, giúp tránh cache cũ sau khi cập nhật plugin.
Restart PHP-FPM để áp dụng:
sudo systemctl restart php8.3-fpm
Verify opcache: Tạo file info.php trong web root:
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
Truy cập http://your-vps-ip/info.php, tìm section "Opcache", thấy "Cache hits" tăng sau vài request là OK. Xoá file info.php ngay sau kiểm tra:
sudo rm /var/www/html/info.php
Bước 5, Cấu hình systemd cho PHP-FPM giới hạn memory
Để tránh PHP-FPM ngốn RAM quá mức (do memory leak), thêm giới hạn cứng trong systemd service.
sudo mkdir -p /etc/systemd/system/php8.3-fpm.service.d
sudo nano /etc/systemd/system/php8.3-fpm.service.d/limits.conf
Thêm nội dung:
[Service]
MemoryMax=1536M
MemoryHigh=1280M
Reload systemd và restart:
sudo systemctl daemon-reload
sudo systemctl restart php8.3-fpm
Giải thích: MemoryMax là giới hạn cứng (RAM + swap), MemoryHigh là ngưỡng cảnh báo. Đặt 1536M cho VPS 2GB RAM là hợp lý.
Verify: Xem memory thực tế của PHP-FPM:
systemctl show php8.3-fpm | grep Memory
htop
Trong htop, filter php-fpm, tổng RES memory không vượt quá 1536M.
Xử lý lỗi thường gặp
Lỗi 1: "502 Bad Gateway" sau khi cấu hình pool riêng
Nguyên nhân: Nginx chưa kết nối đúng socket hoặc socket không có quyền đọc.
Chạy sudo ls -la /run/php/php8.3-fpm-wordpress.sock, nếu không thấy file, restart PHP-FPM. Nếu file tồn tại nhưng 502, kiểm tra Nginx config xem fastcgi_pass có khớp không.
Lỗi 2: OOM Killer giết PHP-FPM (log: "Out of memory")
Nguyên nhân: pm.max_children quá cao so với RAM.
Giải pháp: Giảm max_children hoặc giảm memory consumption của opcache. Chạy journalctl -u php8.3-fpm để xem log chi tiết.
Lỗi 3: Opcache không cache file mới (cache miss cao)
Nguyên nhân: max_accelerated_files quá nhỏ.
Kiểm tra số file thực tế: php -r "opcache_get_status()['memory_usage']['used_memory'];". Nếu gần 256MB, tăng lên 512MB và reload.
Bảng tham số tối ưu cho VPS phổ biến
| RAM VPS | pm.max_children | opcache.memory_consumption | MemoryMax (systemd) |
|---|---|---|---|
| 1GB | 10-12 | 128MB | 768M |
| 2GB | 23 | 256MB | 1536M |
| 4GB | 45-50 | 512MB | 3G |
| 8GB | 80-100 | 1024MB | 6G |
Kết luận
Tối ưu PHP-FPM không cần phần cứng đắt tiền, chỉ cần tính toán đúng số worker dựa trên RAM, bật opcache và giới hạn memory bằng systemd. Với các bước trên, một VPS NVMe 2GB RAM có thể chạy WordPress với 20+ request đồng thời mà không bị chậm hay crash. Nếu bạn đang dùng VPS Linux giá rẻ, hãy áp dụng ngay cấu hình pool riêng và kiểm tra lại memory spike sau 1 tuần. Đừng quên theo dõi log PHP-FPM thường xuyên để điều chỉnh khi traffic thay đổi.
Làm sao để biết mỗi worker PHP-FPM dùng bao nhiêu MB?
Chạy ps aux | grep 'php-fpm' | awk '{sum+=$6} END {print sum/NR}', kết quả là trung bình RSS (KB) mỗi worker. Chia cho 1024 để ra MB.
Nên dùng pm = static hay dynamic cho WordPress?
Nếu traffic ổn định (vd blog, site doanh nghiệp), static giúp memory ít dao động. Nếu traffic theo mùa vụ, dùng dynamic với pm.min_spare_servers = 4 và pm.max_spare_servers = 12.
Opcache có ảnh hưởng đến quá trình cập nhật WordPress không?
Có, nếu revalidate_freq quá cao (vd 3600 giây), bạn sẽ thấy phiên bản cũ sau khi cập nhật plugin. Giá trị 120 giây là cân bằng giữa performance và độ tươi của cache.
Tại sao VPS của tôi báo OOM dù đã đặt max_children đúng?
Có thể do plugin WordPress dùng memory leak hoặc MySQL/Redis cũng ngốn RAM. Hãy kiểm tra ps aux --sort=-%mem | head -20 để xem process nào chiếm nhiều nhất.
Có cần dùng opcache cho PHP 8.3 không?
Có, PHP 8.3 đã cải thiện JIT nhưng opcache vẫn là lớp cache quan trọng nhất. JIT chỉ hỗ trợ một số workload, còn opcache ảnh hưởng đến toàn bộ request.


