Nginx is an alternative web server that is fast becoming popular. Nginx is famed for its ability to handle thousands of concurrent connections while using very little memory. In this post I look at how to install Nginx, PHP-FPM, MySQL and WordPress on a brand new CentOS 6.x Linux installation.
Installing Servers
The default CentOS repositories do not have Nginx so we need to add the Atomic Corp. repository. Adding this repository is easy. As root type:
wget -qO - http://www.atomicorp.com/installers/atomic | sh |
Accept the license that shows up on-screen and it will install the repo for you.
To begin installing Nginx, PHP-FPM and MySQL you need to type the following as root:
yum install nginx php-fpm php-gd php-mysqlnd php-mbstring php-xmlrpc mysql mysql-server |
After the above has been installed it’s time to begin configuring stuff.
Configuring MySQL
Let’s begin with MySQL. Start up the MySQL service and set it to run at boot:
service mysqld start chkconfig mysqld on |
Then do a secure installation to set the root password:
mysql_secure_installation |
Login into the mysql shell using the root password:
mysql -p -uroot |
And add a database and user for WordPress:
CREATE DATABASE wp; GRANT ALL privileges ON wp.* TO wpdbuser@localhost IDENTIFIED BY 'enter_password_here'; |
Adding a user
Next let’s add a user to our system. This user will own the WordPress installation. As root:
useradd wpuser |
Also create directories for website files:
su wpuser cd ~ mkdir www mkdir www/example.com |
All websites owned by this user will be stored in sub-directories under the /home/wpuser/www directory.
You also need to add nginx to the user’s group so that Nginx can access files in the user’s directory. As root:
gpasswd -a nginx wpuser cd /home chmod 750 wpuser |
Configuring PHP-FPM
PHP-FPM processes can run in separate process pools. The processes of a single pool all run as a single user on your system. This allows you to isolate PHP processes of different users.
The first step is to disable the default www pool. As root:
cd /etc/php-fpm.d mv www.conf www.disabled |
Then create a new pool configuration file named wpuser.conf in the /etc/php-fpm.d directory and add the following in it:
[wpuser] listen = /var/run/php-fpm/wpuser.sock listen.owner = nginx listen.group = nginx listen.mode = 0660 user = wpuser group = wpuser pm = dynamic pm.max_children = 10 pm.start_servers = 1 pm.min_spare_servers = 1 pm.max_spare_servers = 3 pm.max_requests = 500 chdir = /home/wpuser/www/ php_admin_value[open_basedir] = /home/wpuser/www:/tmp |
A few things are going on here:
- First up you have the pool name wpuser
- Next there is the definition of the unix socket to listen on. PHP-FPM processes can listen on TCP ports or unix sockets. Unix sockets tend to be faster so we are defining a socket and giving it the same permissions as the nginx user so that Nginx can access it.
- Then we are defining the user that the PHP-FPM processes will have. This is the same user we added to the system before. This restricts the PHP-FPM processes to only accessing files that the wpuser has permission to access.
- The pm.* options define how many processes are started and how they are spawned. Fine tuning these is a topic for another article.
- Finally we are specifying a directory to change to when starting up and also limiting access to the wpuser’s www directory.
The directory for the PHP-FPM socket will have to be created:
mkdir /var/run/php-fpm |
Before you can start up PHP-FPM you also have to fine tune some settings in /etc/php.ini:
cgi.fix_pathinfo=0 upload_max_filesize = 64M |
Now you can start up the php-fpm daemon and set it to run at boot up:
service php-fpm start chkconfig php-fpm on |
Nginx Configuration
As root edit the file /etc/nginx/conf.d/default.conf and make sure the following line contains default_server:
listen 80 default_server; |
The above ensures that requests to your server’s IP address don’t end up serving any real website. We want people to use your domain name to access your site.
Next create a new file name example.com.conf in the /etc/nginx/conf.d directory:
server { listen 80; server_name example.com www.example.com; root /home/wpuser/www/example.com ; client_max_body_size 64M; # Deny access to any files with a .php extension in the uploads directory location ~* /(?:uploads|files)/.*\.php$ { deny all; } location / { index index.php index.html index.htm; try_files $uri $uri/ /index.php?$args; } location ~* \.(gif|jpg|jpeg|png|css|js)$ { expires max; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_index index.php; fastcgi_pass unix:/var/run/php-fpm/wpuser.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } |
Let’s see what’s going on here:
- First we have Nginx listen on port 80 for requests for files from the domain name www.example.com and example.com. We are also defining the document root as /home/wpuser/www/example.com.
- Then you have a setting that increases the default max request size to 64MB. This is required so that we can upload large image files in WordPress.
- Next we have a location block that blocks access to php files in the uploads directory. This is a security setting.
- In the two location blocks that follow you have the default index file specified and a block for static files.
- In the last block you are passing .php file requests to PHP-FPM via the unix socket we configured earlier.
Let’s start Nginx now:
service nginx start chkconfig nginx on |
You’ll also want to poke a hole in the firewall to allow web traffic:
iptables -I INPUT -p tcp --dport=80 -j ACCEPT /etc/init.d/iptables save |
WordPress installation
Finally we get to the WordPress installation:
su wpuser cd ~ wget wordpress.org/latest.zip unzip -q latest.zip mv wordpress/* www/example.com/ rm -rf wordpress latest.zip |
Then just visit your site example.com in your browser and follow the on-screen prompts to install WordPress. You will need the MySQL database name, database username and password that you created earlier.
Two security tips to keep in mind when installing WordPress are:
- Set a different database table prefix than the default wp_.
- Set an administrator username that is not “admin”.
Enabling search engine friendly permalinks
To enable search engine friendly permalinks go to WordPress administration > Settings > Permalinks and choose Custom Structure. Then follow the guide here and add a permalink structure like this:
/%postname%/ |
Conclusion
WordPress plus Nginx and PHP-FPM can be a pretty powerful combination in a low memory footprint. It helps to do things right the first time and I’ve tried to show you how to do that in this guide.
Hi, very great tutorial that I followed for using Sockets for my multiple websites.
I managed to get everything up and running, but I would like to make a Request to you on how to install PhpMyAdmin?
For example: 10.10.10.10/phpmyadmin
I cannot find a solution achieving this using the Socket connection model.
This is my phpmyadmin.conf
server {
listen 8080;
server_name 10.10.10.10;
access_log /var/log/nginx/phpmyadmin/access.log;
error_log /var/log/nginx/phpmyadmin/error.log;
root /usr/share/phpMyAdmin;
location / {
index index.php;
}
## Images and static content is treated different
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
access_log off;
expires 360d;
}
location ~ /\.ht {
deny all;
}
location ~ /(libraries|setup/frames|setup/libs) {
deny all;
return 404;
}
location ~ ^/phpmyadmin/(.+\.php)$ {
include /etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_pass unix:/var/run/php-fpm/phpmyadmin.sock;
fastcgi_param SCRIPT_FILENAME /usr/share/phpmyadmin$fastcgi_script_name;
}
}
This is the new pool config file for phpmyadmin:
[root]
listen = /var/run/php-fpm/phpmyadmin.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
user = nginx
group = nginx
pm = dynamic
pm.max_children = 20
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 5
pm.max_requests = 2000
chdir = /usr/share/phpMyAdmin/
php_admin_value[open_basedir] = /usr/share/phpMyAdmin:/tmp
Am I missing something?
What error are you getting? One thing you will want to correct is the port. It should be listen 80. Also there is a discrepancy between the document root and the setting for fastcgi_param SCRIPT_FILENAME. Better to do:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
Hey thanks. It works better now. I would like to ask you: How do you add a second website to the same server? I have to create another user + socket and duplicate the Nginx configuration settings + php-fpm settings ? I’ve done that, but DNS finished propagating and still can’t connect to my server. Can you do a little step-by-step please?
You’ve correctly described the process of adding another site.
Actually you can even simplify it further by reusing the same PHP-FPM pool for multiple related sites. If all the sites belong to the same user there is no reason why they have to use different PHP-FPM pools.
If DNS is an issue you can try editing your hosts file on your local machine to point your domain to your web server’s IP. I wrote a guide on how to do that:
//abdussamad.com/archives/96-How-to-access-your-website-at-your-new-web-host-before-DNS-propagates.html
Hi, i’ve opted for the option of creating a 2nd user and different PHP-FPM pools. Now the thing for the new added website is that Nginx is throwing me a 502 bad gateway error.
Php is running (checked) must be an issue with the fastcgi or Socket? I don’t know. I’ve created a new socket named “marketman.sock” as you can see:
location ~ \.php$ {
try_files $uri =404;
expires off;
fastcgi_read_timeout 900s;
fastcgi_index index.php;
fastcgi_pass unix:/var/run/php-fpm/marketman.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
Any ideas for me ?
The Nginx config you’ve posted above looks good. Post your PHP-FPM config for the marketman pool.
Another thing is that you can check whether PHP-FPM processes are running as username marketman using the ps Linux command.
this is a great guide for anyone interested in nginx. some experience in linux required. saw a huge performance boost from our apache/php-mysql system.
thanks abduss!