Performance tuning#

This documentation applies to Piler enterprise edition 1.8.x

Revision #1

Publication date: 2025-09-26

In the below example, we optimise for a 2 vCPU + 2 GB RAM box for high concurrency without exhausting memory

Tuning nginx#

/etc/nginx/nginx.conf:

events {
    worker_connections 4096;   # worker_connections * worker_processes = max simultaneous connections
    multi_accept on;           # accept as many as possible
    use epoll;                 # (Linux only, usually auto-detected)
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 65;
    keepalive_requests 10000;

    client_max_body_size 4M;
    client_body_buffer_size 128k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 16k;

    # Buffers for fastcgi (PHP-FPM)
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_busy_buffers_size 64k;
    fastcgi_temp_file_write_size 64k;
    fastcgi_connect_timeout 30s;
    fastcgi_send_timeout 30s;
    fastcgi_read_timeout 30s;
}

Tuning PHP#

/etc/php/8.3/fpm/pool.d/www.conf:

; --- Process management ---
pm = dynamic
pm.max_children = 40        ; hard cap on workers, prevents OOM
pm.start_servers = 6        ; reasonable initial pool
pm.min_spare_servers = 6
pm.max_spare_servers = 20

; --- Recycling ---
pm.max_requests = 500       ; recycle workers to avoid leaks

; --- Timeouts ---
request_terminate_timeout = 30s
request_slowlog_timeout = 10s
slowlog = /var/log/php8.3-fpm.slow.log

; --- Logging ---
catch_workers_output = yes

; --- Resource limits ---
rlimit_files = 65535
rlimit_core = 0

; --- PHP INI overrides (per pool) ---
php_admin_value[memory_limit] = 256M   ; cap memory per worker
php_admin_value[max_execution_time] = 30
php_admin_value[post_max_size] = 4M
php_admin_value[upload_max_filesize] = 4M
php_admin_value[expose_php] = 0

; PHP-FPM status page
pm.status_path = /php-fpm-status
ping.path = /ping
ping.response = pong

/etc/php/8.3/fpm/php.ini:

opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256    ; shared memory in MB
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1     ; check file changes
opcache.revalidate_freq=2
opcache.fast_shutdown=1

realpath_cache_size=4096k
realpath_cache_ttl=600

Key points:#

pm.max_children = 40 → with 2 GB RAM and ~30–40 MB per worker under search load, this is safe.

pm.max_requests = 500 → prevents long-lived workers from fragmenting memory.

Memory cap (memory_limit = 256M) → ensures no runaway scripts eat too much.

rlimit_files raised → nginx + php-fpm under load can otherwise hit "Too many open files" error.

Enable php-fpm status#

/etc/piler/piler-nginx:

location ~ ^/(php-fpm-status|ping)$ {
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    allow 127.0.0.1;
    deny all;
}

Check metrics:

curl http://127.0.0.1/php-fpm-status

The result is something like

pool:                 www
process manager:      dynamic
start time:           15/Sep/2025:12:00:00 +0000
accepted conn:        10234
listen queue:         0
max children reached: 0
active processes:     12
idle processes:       8

Key fields:

listen queue > 0 → requests are waiting (too few workers).

max children reached > 0 → all workers were busy (raise pm.max_children).

active processes close to max_children under load → you’re at capacity.