Nginx and PHP-FPM Configuration and Optimizing Tips and Tricks

nginx-logoI wrote before a guide Howto install Nginx/PHP-FPM on Fedora 20/19, CentOS/RHEL 6.5/5.10, but this guide is just installation guide and many cases Nginx and PHP-FPM basic configuration is good enough, but if you want to squeeze all the juice out of your VPS or web server / servers and do your maintenance work little bit easier, then this guide might be useful. These tips are based entirely on my own experience, so they may not be an absolute truth, and in some situations, a completely different configuration may work better. It’s also good to remember leave resources for another services also if you run example, MySQL, PostgreSQL, MongoDB, Mail server, Name server and/or SSH server on same machine.

And yes here we go…

Nginx Configuration and Optimizing Tips and Tricks

Nginx Tip 1. – Organize Nginx Configuration Files

Normally Nginx configuration files are located under /etc/nginx path.
One good way to organize configuration files is use Debian/Ubuntu Apache style setup:

## Main configuration file ##
/etc/nginx/nginx.conf
 
## Virtualhost configuration files on ##
/etc/nginx/sites-available/
/etc/nginx/sites-enabled/
 
## Other config files on (if needed) ##
/etc/nginx/conf.d/

Virtualhost files have 2 paths, because sites-available directory can contain any stuff, like test configs, just copied/created configs, old configs and so on. And sites-enabled contains only really enabled configurations, actually just only symbolic links to sites-available directory.

Remember add following includes at the end of your nginx.conf file:

## Load virtual host conf files. ##
include /etc/nginx/sites-enabled/*;
 
## Load another configs from conf.d/ ##
include /etc/nginx/conf.d/*;

Nginx Tip 2. – Determine Nginx worker_processes and worker_connections

Default setup is okay for worker_processes and worker_connections, but these values could be little bit optimized:
max_clients = worker_processes * worker_connections

Just Nginx basic setup can handle hundreds of concurrent connection:

worker_processes  1;
worker_connections  1024;

Normally 1000 concurrent connection / per one server is good, but sometimes other parts like disks on server might be slow, and it causes that the Nginx is locked on I/O operations. To avoid locking use example following setup: one worker_precess / per processor core, like:
Worker Processes

worker_processes [number of processor cores];

To check how many processor cores do you have, run following command:

cat /proc/cpuinfo |grep processor
processor	: 0
processor	: 1
processor	: 2
processor	: 3

So here is 4 cores and worker_processes final setup could be following:

worker_processes 4;

Worker Connections
Personally I stick with 1024 worker connections, because I don’t have any reason to raise this value. But if example 4096 connections per second is not enough then it’s possible to try to double this and set 2048 connections per process.

worker_processes final setup could be following:

worker_connections 1024;

I have seen some configurations where server admins are used too much Apache and think if I set Nginx worker_processes to 50 and worker_connections to 20000 then my server could handle all traffic once what we get monthly…but yes it’s not true. It’s just wasting of resources and might cause some serious problems…

Nginx Tip 3. – Hide Nginx Server Tokens / Hide Nginx version number

This is good for security reasons hide server tokens / hide Nginx version number, especially, if run some outdated version of Nginx. This is very easy to do just set server_tokens off under http/server/location section, like:

server_tokens off;

Nginx Tip 4. – Nginx Request / Upload Max Body Size (client_max_body_size)

If you want to allow users upload something or upload personally something over the HTTP then you should maybe increase post size. It can be done with client_max_body_size value which goes under http/server/location section. On default it’s 1 Mb, but it can be set example to 20 Mb and also increase buffer size with following configuration:

client_max_body_size 20m;
client_body_buffer_size 128k;

If you get following error, then you know that client_max_body_size is too low:
“Request Entity Too Large” (413)

Nginx Tip 5. – Nginx Cache Control for Static Files (Browser Cache Control Directives)

Browser caching is import if you want save resources and bandwith. It’s easy setup with Nginx, following is very basic setup where logging (access log and not found log) is turned off and expires headers are set to 360 days.

location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
    access_log        off;
    log_not_found     off;
    expires           360d;
}

If you want more complicated headers or some other expiration by filetypes then you could configure those separately.

Nginx Tip 6. – Nginx Pass PHP requests to PHP-FPM

Here you could use default tpc/ip stack or use directly Unix socket connection. You have to also setup PHP-FPM listen exactly same ip:port or unix socket (with Unix socket also socket permission have to be right). Default setup is use ip:port (127.0.0.1:9000) you could of course change ips and ports what PHP-FPM listens. Here is very basic configuration with Unix socket example commented out:

# Pass PHP scripts to PHP-FPM
location ~* \.php$ {
    fastcgi_index   index.php;
    fastcgi_pass    127.0.0.1:9000;
    #fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
    include         fastcgi_params;
    fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
}

It’s also possible to run PHP-FPM another server and Nginx another.

Nginx Tip 7. – Prevent (deny) Access to Hidden Files with Nginx

It’s very common that server root or other public directories have hidden files, which starts with dot (.) and normally those is not intended to site users. Public directories can contain version control files and directories, like .svn, some IDE properties files and .htaccess files. Following deny access and turn off logging for all hidden files.

location ~ /\. {
    access_log off;
    log_not_found off; 
    deny all;
}

PHP-FPM Configuration Tips and Tricks

PHP-FPM Tip 1. – PHP-FPM Configuration files

Normally PHP-FPM configuration files are located on /etc/php-fpm.conf file and /etc/php-fpm.d path. This is normally excellent start and all pool configs goes to /etc/php-fpm.d directory. You need to add following include line on your php-fpm.conf file:

include=/etc/php-fpm.d/*.conf

PHP-FPM Tip 2. – PHP-FPM Global Configuration Tweaks

Set up emergency_restart_threshold, emergency_restart_interval and process_control_timeout. Default values for these options are totally off, but I think it’s better use these options example like following:

emergency_restart_threshold 10
emergency_restart_interval 1m
process_control_timeout 10s

What this mean? So if 10 PHP-FPM child processes exit with SIGSEGV or SIGBUS within 1 minute then PHP-FPM restart automatically. This configuration also sets 10 seconds time limit for child processes to wait for a reaction on signals from master.

PHP-FPM Tip 3. – PHP-FPM Pools Configuration

With PHP-FPM it’s possible to use different pools for different sites and allocate resources very accurately and even use different users and groups for every pool. Following is just example configuration files structure for PHP-FPM pools for three different sites (or actually three different part of same site):

/etc/php-fpm.d/site.conf
/etc/php-fpm.d/blog.conf
/etc/php-fpm.d/forums.conf

Just example configurations for every pool:
/etc/php-fpm.d/site.conf

[site]
listen = 127.0.0.1:9000
user = site
group = site
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-site.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

/etc/php-fpm.d/blog.conf

[blog]
listen = 127.0.0.1:9001
user = blog
group = blog
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-blog.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 200
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

/etc/php-fpm.d/forums.conf

[forums]
listen = 127.0.0.1:9002
user = forums
group = forums
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-forums.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 400
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

So this is just example howto configure multiple different size pools.

PHP-FPM Tip 4. – PHP-FPM Pool Process Manager (pm) Configuration

Best way to use PHP-FPM process manager is use dynamic process management, so PHP-FPM processes are started only when needed. This is almost same style setup than Nginx worker_processes and worker_connections setup. So very high values does not mean necessarily anything good. Every process eat memory and of course if site have very high traffic and server lot’s of memory then higher values are right choise, but servers, like VPS (Virtual Private Servers) memory is normally limited to 256 Mb, 512 Mb, 1024 Mb. This low RAM is enough to handle even very high traffic (even dozens of requests per second), if it’s used wisely.

It’s good to test how many PHP-FPM processes a server could handle easily, first start Nginx and PHP-FPM and load some PHP pages, preferably all of the heaviest pages. Then check memory usage per PHP-FPM process example with Linux top or htop command. Let’s assume that the server has 512 Mb memory and 220 Mb could be used for PHP-FPM, every process use 24 Mb RAM (some huge content management system with plugins can easily use 20-40 Mb / per PHP page request or even more). Then simply calculate the server max_children value:
220 / 24 = 9.17

So good pm.max_children value is 9. This is based just quick average and later this could be something else when you see longer time memory usage / per process. After quick testing it’s much easier to setup pm.start_servers value, pm.min_spare_servers value and pm.max_spare_servers value.

Final example configuration could be following:

pm.max_children = 9
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200

Max request per process is unlimited by default, but it’s good to set some low value, like 200 and avoid some memory issues. This style setup could handle large amount of requests, even if the numbers seems to be small.

Do you have problems or some nice Nginx and PHP-FPM tips and trick?

Please feel free to post your questions and tips and tricks here.

Follow If Not True Then False Updates!

125 Comments

  1. Thanks a lot for all these tips. I was configuring my low memory VPS server and I noticed fpm use lot of memory. After tuning with your posts I’ve half of the memory free with all websites installed :)
    Cheers

    • Hi Maco,

      Excellent! :)

      Yes, in principle PHP-FPM uses as much memory as you give to it. If you use large PHP frameworks or software, then some kind of proxy/cache is best idea to reduce PHP load from server(s) and speed up things.

  2. ;)

    Yes I’m using W3C total cache on WordPress, but I think I’ll try memcached next days… even if I really don’t need it (I’ve 200 visits per day… so not a big traffic).

    I’m not really sure how many users at the same time I can support with the configuration I made:
    pm.max_children = 4
    pm.start_servers = 2
    pm.min_spare_servers = 1
    pm.max_spare_servers = 2
    pm.max_requests = 200

    with, on nginx:
    worker_connections 1024;

    Normally nginx supports 1024 connection simultaneously with max 4 using php… I think if php responses are fast 4 could be good for almost of “personal” blog on the net.

    Anyway a stress test with BlazeMeter say the configuration if good with 50 (!!) concurrents users receiving responses in a reasonable time :)

    Thanks a lot… I’ll ping back this article on my post blog talking about all my tests…

    • Hi Marco,

      Your configuration looks reasonable.

      Repeatedly I have seen configurations where is very high numbers on pm.* section. It’s okay if you have example 16 cores and lot of memory, but with low-end boxes result is not so good. At the worst case PHP-FPM uses all processor capacity and memory what you have and try to generate all requested pages same time. With reasonable low values PHP-FPM adds requests to queue and process those requests as soon as possible. Normally every users get their pages, but if there is some traffic peaks, then waiting time might be little bit longer.

      So many cases less is more. :)

  3. hi , do have any good configuration for php-fpm.
    for RAM 2gb on middle high traffic site.

    thank you~

    • Hi Paddy,

      I don’t have any “ready” configuration for you, because it depends lot of application(s) what you are running, how much every page generation needs ram and what else you are running on your server.

      Could you tell little bit more?

  4. Hi Paul

    I’m currently setting up Apple ProfileManager 2 (to manage iPads and iPhones) proxied through Nginx. I’m new to Nginx so thank you so much for this article as, I think I might have had a lightbulb moment. Would something like this work?

    server {
    listen 80;
    server_name profilemanger.example.com;
    return 301 https://$host$request_uri;
    }
    server {
    listen 443;
    server_name profilemanager.example.com;
    root /var/www;
    index index.php;
    # Pass PHP scripts to PHP-FPM
    location ~* \.php$ {
    fastcgi_index index.php;
    fastcgi_pass 172.21.11.52:9000;
    #fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    }
    }
    location ~ /\.ht {
    deny all;
    }
    }

    I haven’t include any certificate directives because the profilemanger has its own certificates – is this the correct way to handle this?

    I presume I have to have a fast_cgi params file?

    Regards

    Stephen

  5. Hi JR!
    Thank you for this great post.
    However there is something that I don’t understand how to set the pm.max_children, pm.start_servers, pm.min_spare_servers and pm.max_spare_servers…
    I have an Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz with 8 cores and 48GB of RAM.
    I have set:
    pm.max_children = 1000
    pm.start_servers = 245
    pm.min_spare_servers = 45
    pm.max_spare_servers = 350

    I want at least 1000 concurrents users.
    But I have some error saying me that i have too mane processes…
    What’s wrong?
    Thank you very Much!
    Josh

    • Hi Josh,

      First, you have excellent server and handling 1000 concurrent users shouldn’t be problem. Of course depending your network and content.

      Then, I have a few questions to you:

      1. Could you post values of following nginx parameters:
      worker_processes
      worker_connections

      2. What app you are running, how much every php processes use memory?

      3. Do you use currently any caching or is it possible use any caching?

      4. What else is running on this same server (some db, background processes etc.)?

  6. Great article, thank you!

  7. Hi.

    First of all thanks for your article it is really good.

    Now I need to show you somthing.

    I write an “real time” application over nginx + php-fpm on an amazon ec2 cloud. I use ajax to ask every 0.3 to 0.8 some information. On high load moments, there are 50 to 75 concurrent users, everyone making 1 or 2 requests every 0.3 to 0.8 seconds, so it is more or less 500 to 750 requests per seconds on the moments whith the highest loads.

    The ec2 instance is a large type instance whith intel xeon 1.8ghz with 2 cores and 8GB ram and it is fully dedicated to this web application.

    On my nginx config file the worker_processes is set to auto and worker_connections to 2048 and pm parameters are as follows:
    pm = dynamic
    pm.max_children = 250
    pm.start_servers = 50
    pm.min_spare_servers = 20
    pm.max_spare_servers = 50
    pm.max_requests = 5000

    On some moments on the ajax requests the server response is 503 error due to back end capacity.

    From your point of view, this configuration is enought to support our environment?

    Aditionally, the system is behind a load balancer that redirect the request to 3 instances like I mention before.

    Please, I hope you can give me some advice about it.

    kind regards!

    • Hi Chemi,

      First you are very welcome! Then let’s check some useful background information.

      Do you have php-fpm status page enabled? If yes, could you post current status? Or if not, could you enable it, run your system some time and post then current status?

      You can use some subdomain or other domain for status page, if you don’t want to touch your site production config files.

      You have to enable status page from php-fpm pool config (uncomment pm.status_path = /status) and use something like following on your nginx config:

      location = /status {
          access_log off;
          allow 127.0.0.1;
          # Replace following with your ip (this keeps your status page private) 
          allow xxx.xxx.xxx.xxx;
          deny all;
          include /etc/nginx/fastcgi_params; 
          fastcgi_param SCRIPT_FILENAME /status; 
          fastcgi_pass 127.0.0.1:9000; 
      }

      You need to reload/restart your php-fpm and nginx.

      Then could you post output of following command, when everything is running and you have users using your service:

      ps -ylC php-fpm --sort:rss

      And could you also post few 503 error lines, from log?

  8. Thanks for your tips… I’m a really newbie in setting up php-fpm.

    The status page was not enable, but I just enable it and can post it when I get some troubles.

    Respect the pm parameter, I think I had an error,

    My pm.max_spare_server was 50 but max_spare is 250… this was a limit when the load of the servers is near to this value and it don’t scale correctly, causing a 503 error.

    I set it up to 250 and I hope it resolve the problems.

    Thanks a lot for your tips again, I learn a lot about php-fpm set up, and I’ll post my conclusions when I can test it on a high load environment.

    Kind regards.

  9. Hi,

    I enjoyed your post, thanks for detailed info, i am about to set up nginx+php-fpm+mysql on Intel Xeon 4 cores and 24 GB RAM, Do you thing this HW configuration will be enough to handle 2k simultaneous users ?, Using Custom Coded PHP app. What should i use for cache ? so that mysql queries will be cached too and wont cause bottleneck on mysql part. I have read about php-APC , nginx cache , memcached , memcahce, varnish. just cant figure out which one to use and optimize. Its gonna be a simple site, getting data from mysql and showing them using php… Thanks for your time.

  10. Hey I’m newbie in using nginx. here i created some application. there are 27 client want access my application. everybody can access my web, but there is one left can’t log in into my apps. i’ve tried to remove some cache and cookies in mozila. i tried using another web browser and the result is nothing.
    the client access from different segments IP. is there anything i left in configuration nginx or php-fpm for this situation?
    here’s my config nginx in RHEL 6:

    user root;
    #worker_processes 4;
    worker_processes auto;

    error_log logs/error.log;
    error_log logs/error.log notice;
    error_log logs/error.log info;
    error_log logs/error.log warn;
    pid logs/nginx.pid;

    #events {
    # worker_connections 1024;
    #}

    events {
    use epoll;
    worker_connections 2048;
    multi_accept on;
    }

    http {
    include mime.types;
    default_type application/octet-stream;

    #log_format main ‘$remote_addr – $remote_user [$time_local] “$request” ‘
    # ‘$status $body_bytes_sent “$http_referer” ‘
    # ‘”$http_user_agent” “$http_x_forwarded_for”‘;

    #Logging
    #access_log off;
    #access_log /usr/local/nginx/logs/access.log;
    error_log /usr/local/nginx/logs/error.log;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;

    #Timeouts
    keepalive_timeout 65;

    #Setting Buffer
    client_body_buffer_size 10K;
    client_header_buffer_size 1k;
    client_max_body_size 25m;
    large_client_header_buffers 2 1k;

    #Gzip Compression
    gzip on;

    server {
    listen 80;
    #listen 443;
    server_name localhost;
    root /usr/local/nginx/html/losqcodo/;
    index index.php;

    #proxy
    #proxy_hide_header Set-Cookie;
    #proxy_ignore_headers Set-Cookie;
    # important! Remember the special inheritance rules for proxy_set_header:
    # # http://nginx.org/ru/docs/http/ngx_http_proxy_module.html#proxy_set_header
    #proxy_set_header Cookie “”;

    #charset koi8-r;

    #access_log logs/host.access.log main;

    location / {
    try_files $uri $uri/ /index.php;
    #allow 192.168.100.0/24;
    #allow 192.168.0.0/24;
    satisfy any;
    #root html;
    #index index.html index.htm;
    }

    error_page 404 /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    # proxy_pass http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass 127.0.0.1:9000;
    #fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    include fastcgi.conf;
    }

    # deny access to .htaccess files, if Apache’s document root
    # concurs with nginx’s one
    #
    #location ~ /\.ht {
    # deny all;
    #}
    #include /usr/local/nginx/conf/*.conf;
    #include /usr/local/nginx/conf/sites-enabled/*;
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    # listen 8000;
    # listen somename:8080;
    # server_name somename alias another.alias;

    # location / {
    # root html;
    # index index.html index.htm;
    # }
    #}

    # HTTPS server
    #
    #server {
    # listen 443 ssl;
    # server_name localhost;

    # ssl_certificate cert.pem;
    # ssl_certificate_key cert.key;

    # ssl_session_cache shared:SSL:1m;
    # ssl_session_timeout 5m;

    # ssl_ciphers HIGH:!aNULL:!MD5;
    # ssl_prefer_server_ciphers on;

    # location / {
    # root html;
    # index index.html index.htm;
    # }
    #}

    }

    So i need some help to answer this problem please :(

    • Hi Kahfi,

      So website works, but login not? Do you have tested your code with some other web server, like Apache is it working normally? Do you use PHP Sessions?

  11. Do you recommend the VIRT RES or SHR column from top as the Measurement for memory usage of PHP-FPM?

    • Hi ethan,

      Yes VIRT, RES or SHR column from top are good info when you try measure memory usage per php-fpm processes. Actually RES is an accurate representation of how much actual physical memory a process is consuming.

  12. The application has running well, but there’s some problem again. Between nginx and php-fpm the connection sometimes refuse and timeout. Like occur error message in error.log :

    “upstream timed out (110: Connection timed out) while reading response header from upstream ”

    Does anybody knows how to fix this problem ? :)

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackbacks/Pingbacks

  1. Install Nginx/PHP-FPM on Fedora 16/15, CentOS/RHEL 6/5.7 - [...] This is just very simple basic configuration, but if you want configure and optimize Nginx and PHP-FPM then check …
  2. Ideanub – Configuring Multiple Symfony Websites on Nginx with PHP-FPM - [...] hurt to  make a few small changes in the config files to improve the performance. Thanks to JR’s post …
  3. Wordpress + Nginx + php-fpm on OVH VPS | Simply Me - […] create this useful configuration I follow this guide. If !1 then 0 it’s an incredible technical […]