Install and configure Nginx, MySQL & PHP-FPM in CentOS 6

The following is a quick-N-dirty write-up about how to install and configure the LEMP stack (Nginx, MySQL and PHP-FPM) in CentOS 6. I use this as a reference/guide whenever I need to deploy the LEMP stack. I've split the article to the following sections:

  1. Enable EPEL repository
  2. Update the system
  3. Install and configure MySQL
  4. Install and configure Nginx
  5. Install and configure PHP-FPM
  6. Enable and restart the services

0. Enable EPEL repository on the system

if ! type -path "wget" > /dev/null 2>&1; then yum install wget -y; fi

wget -P /tmp rpm -Uvh http://epel.blizoo.mk/epel/6/i386/epel-release-6-8.noarch.rpm
rpm -Uvh /tmp/epel-release-6-8.noarch.rpm
rm -f /tmp/epel-release-6-8.noarch.rpm

1. Make sure the CentOS 6 system is fully up-to-date

yum update

if there is a kernel update, make sure you're booted into it before proceeding further.


2. Install and configure MySQL database server

install MySQL by running

yum install mysql mysql-server

start the MySQL server by

service mysqld restart

set-up the MySQL server using

mysql_secure_installation

answer questions when prompted

  • Enter current password for root (enter for none):
  • Set root password? [Y/n] y
  • Remove anonymous users? [Y/n] y
  • Disallow root login remotely? [Y/n] y
  • Remove test database and access to it? [Y/n] y
  • Reload privilege tables now? [Y/n] y

next, edit /etc/my.cnf and add bind-address = 127.0.0.1 within the [mysqld] block

if ! type -path "vim" > /dev/null 2>&1; then yum install vim -y; fi

vim /etc/my.cnf

[mysqld]
bind-address = 127.0.0.1
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

3. Install and configure Nginx HTTP server

install Nginx using yum and make a copy of the default nginx.conf configuration file

yum install nginx
cd /etc/nginx/
cp nginx.conf{,.orig}

set-up Nginx's main configuration file so it looks like the following

cat > nginx.conf

user              nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
#error_log  /var/log/nginx/error.log  info;

pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    server_names_hash_bucket_size 64;
    include       /etc/nginx/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"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  30;
    server_tokens off;

    gzip on;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;

    # enabled sites
    include /etc/nginx/sites-enabled/*;
}

basically, worker_processes is determined by the number of CPU's the machine has and you can use the following command to find what number to use there

grep -c 'model name' /proc/cpuinfo

also, the main configuration file includes /etc/nginx/sites-enabled/* which is the location of the server-blocks that are going to be set-up later on. I choose to play the Debian-way by having the files created in sites-available and then linking the ones I want enabled to sites-enabled

mkdir /etc/nginx/{sites-available,sites-enabled}
rm -f /etc/nginx/conf.d/*

cat > /etc/nginx/conf.d/caches.conf

## caches
location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ {
    access_log off;
    expires max;
}
location ~* \.(js)$ {
    access_log  off;
    log_not_found   off;
    expires     7d;
}
location ~* \.(woff|svg)$ {
    access_log  off;
    log_not_found   off;
    expires     30d;
}

set-up default server block

cat > /etc/nginx/sites-available/default

server {
    listen       80 default_server;
    server_name  _;
    root   /srv/www/default;

    location / {
        index  index.html index.htm;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /srv/www/default;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /srv/www/default;
    }
}

set-up default files

mkdir -p /srv/www/default

CONTENT='<!DOCTYPE html>
<html lang="en">
<head>
        <title>ViruSzZ</title>
</head>
<body style="background:#000;color:#fff;">
        <div style="color:#fff;width:100%;">
                <h1 align="center">you feel the silence?</h1>
        </div>
</body>
</html>'

echo ${CONTENT} > /srv/www/default/index.html

------

CONTENT='<!DOCTYPE html>
<html lang="en">
<head>
        <title>404</title>
</head>
<body style="background:#000;color:#fff;">
        <div style="color:#fff;width:100%;">
                <h1 align="center">404 straight in your face</h1>
        </div>
</body>
</html>'

echo ${CONTENT} > /srv/www/default/404.html

------

CONTENT='<!DOCTYPE html>
<html lang="en">
<head>
        <title>Whoooopsssss</title>
</head>
<body style="background:#000;color:#fff;">
        <div style="color:#fff;width:100%;">
                <h1 align="center">Whoooopssssssy... Something wrong happend back here!</h1>
        </div>
</body>
</html>'

echo ${CONTENT} > /srv/www/default/50x.html
unset CONTENT

set-up server block for laravel (PHP) based web application

cat > /etc/nginx/sites-available/d.stavrovski.net

server {
        listen 80;
        server_name www.d.stavrovski.net daniel.stavrovski.net;
        rewrite ^(.*) http://d.stavrovski.net$1 permanent;
}
server {
        listen 80;
        server_name d.stavrovski.net;
        access_log /var/log/nginx/d.stavrovski.net.log;
        error_log /var/log/nginx/d.stavrovski.net-error error;

        root /srv/www/d.stavrovski.net/public;
        index  index.html index.php;

        ### root directory ###
        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        ### security ###
        error_page 403 =404;

        ### security ###
        location ~* ^/uploads/.*.(html|htm|shtml|php)$ {
                types { }
                default_type text/plain;
        }

        #  location ~* secure-uri {
        #      allow <YOUR_IP>;
        #      allow 127.0.0.1;
        #      deny all;
        #  }

        ### disable logging ###
        location = /robots.txt { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; }

        ### caches ###
        include /etc/nginx/conf.d/caches.conf;

        ### php block ###
        location ~ \.php?$ {
                try_files $uri =404;
                include fastcgi_params;
                #fastcgi_pass 127.0.0.1:9001;
                fastcgi_pass unix:/var/run/php-main.socket;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_intercept_errors on;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                #Prevent version info leakage
                fastcgi_hide_header X-Powered-By;
        }
}

set-up server block for WordPress based web application

cat > /etc/nginx/sites-available/d.stavrovski.net-wordpress

server {
        listen 80;
        server_name www.d.stavrovski.net;
        rewrite ^(.*) http://d.stavrovski.net$1 permanent;
}
server {
        listen 80;
        server_name d.stavrovski.net;

        client_max_body_size 5m;
        client_body_timeout 60;

        access_log /var/log/nginx/d.stavrovski.net.log;
        error_log /var/log/nginx/d.stavrovski.net-error error;

        root /srv/www/d.stavrovski.net/wordpress;
        index  index.html index.php;

        ### root directory ###
        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        ### security ###
        error_page 403 =404;
        location ~ /\. { access_log off; log_not_found off; deny all; }
        location ~ ~$ { access_log off; log_not_found off; deny all; }
        location ~* wp-admin/includes { deny all; }
        location ~* wp-includes/theme-compat/ { deny all; }
        location ~* wp-includes/js/tinymce/langs/.*\.php { deny all; }
        location /wp-includes/ { internal; }
        #location ~* wp-config.php { deny all; }
        location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php)$ {
                types { }
                default_type text/plain;
        }

        #  location ~* wp-admin {
        #      allow <YOUR_IP>;
        #      allow 127.0.0.1;
        #      deny all;
        #  }

        ### disable logging ###
        location = /robots.txt { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; }

        ### caches ###
        include /etc/nginx/conf.d/caches.conf;

        ### php block ###
        location ~ \.php?$ {
                try_files $uri =404;
                include fastcgi_params;
                #fastcgi_pass 127.0.0.1:9001;
                fastcgi_pass unix:/var/run/php-main.socket;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_intercept_errors on;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                #Prevent version info leakage
                fastcgi_hide_header X-Powered-By;
        }
}

with all this in place, let's proceed with enabling the server-blocks and starting Nginx up

cd /etc/nginx/sites-enabled/
for d in ../sites-available/*; do ln -s /etc/nginx/sites-available/${d##*/}; done
nginx -t
service nginx restart

4. Install and configure PHP-FPM server

yum install php-fpm php-mysql

also, install any other PHP module that your application requires. the list is as follows

APC (php-pecl-apc)
|_> APC caches and optimizes PHP intermediate code

CLI (php-cli)
|_> Command-line interface for PHP

PEAR (php-pear)
|_> PHP Extension and Application Repository framework

PDO (php-pdo)
|_> A database access abstraction module for PHP applications

MySQL (php-mysql)
|_> A module for PHP applications that use MySQL databases

PostgreSQL (php-pgsql)
|_> A PostgreSQL database module for PHP

MongoDB (php-pecl-mongo)
|_> PHP MongoDB database driver

SQLite (php-sqlite)
|_> Extension for the SQLite V2 Embeddable SQL Database Engine

Memcache (php-pecl-memcache)
|_> Extension to work with the Memcached caching daemon

Memcached (php-pecl-memcached)
|_> Extension to work with the Memcached caching daemon

GD (php-gd)
|_> A module for PHP applications for using the gd graphics library

XML (php-xml)
|_> A module for PHP applications which use XML

MBString (php-mbstring)
|_> A module for PHP applications which need multi-byte string handling

MCrypt (php-mcrypt)
|_> Standard PHP module provides mcrypt library support

next, set-up PHP's main configuration file /etc/php.ini

vim /etc/php.ini

:%s#;cgi.fix_pathinfo=1#cgi.fix_pathinfo=0#
:%s#;date.timezone =#date.timezone = Europe/Skopje#
:%s#memory_limit = 128M#memory_limit = 64M#
:%s#expose_php = On#expose_php = Off#

and edit PHP-FPM's main configuration file /etc/php-fpm.conf

vim /etc/php-fpm.conf

:%s#;emergency_restart_threshold = 0#emergency_restart_threshold = 10#
:%s#;emergency_restart_interval = 0#emergency_restart_interval = 1m#
:%s#;process_control_timeout = 0#process_control_timeout = 10#

next, I'm going to create PHP-FPM's pools that I used in the server-blocks before. feel free to tune these to suit your needs.

cp /etc/php-fpm.d/www.conf{,.orig}
cat > /etc/php-fpm.d/www.conf

[main-site]
;listen = 127.0.0.1:9001
listen = /var/run/php-main.socket
user = nginx
group = nginx
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/main.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
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_admin_value[error_log] = /var/log/php-fpm/main-error.log
php_admin_flag[log_errors] = on

finally, install and set-up APC for better performance

yum install php-pecl-apc

and create the following configuration file

cat > /etc/php.d/apc.ini

extension = apc.so
apc.enabled=1
apc.shm_segments=1
apc.shm_size=128M
apc.num_files_hint=1024
apc.user_entries_hint=4096
apc.ttl=7200
apc.use_request_time=1
apc.user_ttl=7200
apc.gc_ttl=3600
apc.cache_by_default=1
apc.filters
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.file_update_protection=2
apc.enable_cli=0
apc.max_file_size=1M
apc.stat=1
apc.stat_ctime=0
apc.canonicalize=0
apc.write_lock=1
apc.report_autofilter=0
apc.rfc1867=0
apc.rfc1867_prefix =upload_
apc.rfc1867_name=APC_UPLOAD_PROGRESS
apc.rfc1867_freq=0
apc.rfc1867_ttl=3600
apc.include_once_override=0
apc.lazy_classes=0
apc.lazy_functions=0
apc.coredump_unmap=0
apc.file_md5=0
apc.preload_path

5. Enable and restart the services

check Nginx's configuration file by running

nginx -t

restart Nginx

service nginx restart

restart PHP-FPM

service php-fpm restart

restart MySQL

service mysqld restart

enable the services on system startup

for s in nginx php-fpm mysqld; do chkconfig $s on; done

enjoy and take care