WordPress webdesign and development in Houston Texas

WordPress Performance Server

Using this setup you will have the ability to host a large high performance WordPress Multisite installation or numerous single installs

WordPress Performance Server - Debian “squeeze” with Nginx, APC and PHP from the Dotdeb repos


Page 5 the .conf files - configuring your web server.

The default .conf files included in the Dotdeb packages will almost work right out of the box. You will need to make a few adjustments.

Nginx and PHP-FPM
Before you set up the Nginx.conf files you need to decide your folder structure and where the root of your websites will live. I like to use /srv/www/sitename/public for the web root and /srv/www/sitename/logs for the error and access logs. The files in your web root need to be owned by the same user as Nginx runs under. The Debian way is to run the server as www-data.

mkdir /srv/www/yoursitename /srv/www/yoursitename/public /srv/www/yoursitename/logs
chown www-data:www-data /srv/www/yoursitename/public/ -R

File: /etc/nginx/nginx.conf
Note: be wary of some of the copy and paste guides out there. The Slicehost and Linode tutorials are out of date and contain security vulnerabilities. If you don’t understand something question it and do your own research. The following files have been tested and are in use on real production servers.

user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
events {
	worker_connections 1024;
	# multi_accept on;
}
http {
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	server_tokens off;
	include mime.types;
	default_type  application/octet-stream;
	index index.php index.htm index.html redirect.php;
	#Gzip
	gzip  on;
	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_disable "MSIE [1-6].(?!.*SV1)";
	gzip_types text/plain text/css application/json application/x-javascript text/xml
                        application/xml application/xml+rss text/javascript;
	#FastCGI
	fastcgi_intercept_errors on;
	fastcgi_ignore_client_abort on;
	fastcgi_buffers 8 16k;
	fastcgi_buffer_size 32k;
	fastcgi_read_timeout 120;
	fastcgi_index  index.php;
	limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
	##
	# Virtual Host Configs
	##
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;  #Our individual site vhost server files will live here
}

If your familiar with the Apache Vhost concept you will be fine with Nginx. The Nginx server files we create act as individual site vhost.conf files and can either be in one large file or for better organization you can create a separate file for each site. The Debian way is to create 2 directories under /etc/nginx/, sites-available for the files and sites-enabled for sym links pointing to the file in sites-available. This allows you to turn off a site without having to delete it if you ever need it again.

touch /etc/nginx/sites-available/your_domain.com
ln -s /etc/nginx/sites-available/your_domain.com /etc/nginx/sites-enabled/your_domain.com
cd /etc/nginx/sites-enabled
ls -a
:drwxr-xr-x 2 root root 4096 Jun 26 02:04 .
drwxr-xr-x 5 root root 4096 Jun 26 01:50 ..
lrwxrwxrwx 1 root root   40 Jun 26 01:59 your_domain.com -> /etc/nginx/sites-available/your_domain.com

File: /etc/nginx/sites-available/your_domain.com
This is for a single WordPress install. We are also including the W3 Total Cache Nginx rewrite rules in a separate file w3-total.conf. You will need to run touch /etc/nginx/w3-total.conf and chown www-data:www-data /etc/nginx/w3-total.conf and list the path in the W3 Total Cache general settings so it can write to the file. *Note: Anytime you make a change to an Nginx server file you will need to restart.

server {
	listen 80;
	server_name your_domain.com www.your_domain.com;
	root /srv/www/domain/public;
	access_log /srv/www/domain/logs/access.log;
	error_log /srv/www/domain/logs/error.log;
        client_max_body_size 8M;
        client_body_buffer_size 128k;
        #The section below contains your WordPress rewrite rules
	location / {
                try_files $uri $uri/ /index.php?q=$uri&$args;
	}
        location /search { limit_req zone=one burst=3 nodelay; rewrite ^ /index.php; }
	fastcgi_intercept_errors off;
	location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
		expires max;
		add_header Pragma public;
		add_header Cache-Control "public, must-revalidate, proxy-revalidate";
	}
#sample 301 redirect
#	location /2011/06/26/permalink/ {
#		rewrite //2011/06/26/permalink/ http://example.com/2011/06/27/permalink_redirecting_to/ permanent;
#		}
#Send the php files to upstream to PHP-FPM
#This can also be added to separate file and added with an include
location ~ \.php {
        try_files $uri =404; #This line closes a big security hole
                             #see: http://forum.nginx.org/read.php?2,88845,page=3
        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx;
        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;
        fastcgi_pass 127.0.0.1:9000;
}
#Once you have your w3-total.conf file ready un-comment out the line below
#include w3-total.conf;

You can now start your server and see if it works.

touch /srv/www/yoursite/public/index.php
echo '<?php echo "<h1>Nginx Baby - It WORKS!!!!</h1>"; ?>' >> /srv/www/yoursite/public/index.php
/etc/init.d/nginx start

For more Nginx server file examples see the WordPress.org Nginx Codex

Open a browser and go to your domain or ip and if you did everything right you should see:

WordPress Multisite, alternative configuration and multiple non WordPress vhosts
If you want to host a multisite setup or add additional websites to your server or VPS you can include the WordPress Multisite rewrite rules in a separate file and simplify your per site vhosts.

File: /etc/nginx/nginx.conf

<pre>user www-data;
worker_processes  10;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
	include mime.types;
	default_type       application/octet-stream;
	access_log         /var/log/nginx/access.log;
	sendfile           on;
#	tcp_nopush         on;
	keepalive_timeout  3;
#	tcp_nodelay        on;
#	gzip               on;
	client_max_body_size 13m;
	index              index.php index.html index.htm;
	upstream php {
	#	server 127.0.0.1:9000;
		server unix:/tmp/php-fpm.sock;
	}
	include sites-enabled/*;
}

This is a sample vhost for a subdomain WordPress Multisite that uses the Domain Mapping Plugin along with a few static sites that are also hosted on the server.

server {
	 listen 80;
	#Add a server_name entry for each mapped domain
	server_name star.main-multisite-domain.com *.main-multisite.com;
        server_name mapped-domain.com www.mapped-domain.com;
	server_name mapped-domain-2.com www.mapped-domain-2.com;
	server_name mapped-domain-3.com www.mapped-domain-3.com;
	root /srv/www/wordpress/public;
	access_log /srv/www/wordpress/logs/access.log;
	error_log /srv/www/wordpress/logs/error.log;
	index index.php index.html index.htm;
        #The Multisite rules are in the include below
	include global-wp-multi.conf;
}
#Begin Static Non WP Sites
#Repeat the vhosts below for each additional static site.  Make sure you make each one a root directory
server {
	server_name static-non-wp-domain.com www.static-non-wp-domain.com;
	root /srv/www/static-non-wp-domain/public;
        error_log /srv/www/static-non-wp-domain/logs/error.log;
	index index.php index.html index.htm;
	}

File: /etc/nginx/global-wp-multi.conf
This is a default config file that will handle the rewrite rules for domain mapped subdomain multisites.

error_page 404 = @wordpress;
log_not_found off;
location / {
	try_files $uri $uri/ /index.php?$args;
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
	expires 24h;
	log_not_found off;
}
rewrite ^/files/(.+) /wp-includes/ms-files.php?file=$1 last;
location ^~ /files/ {
	rewrite ^.*/files/(.+)$ /wp-includes/ms-files.php?file=$1 last;
}
include wp-total.conf;
# Rewrite multisite '.../wp-.*' and '.../*.php'.
if (!-e $request_filename) {
	rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
	rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
}
location @wordpress {
        fastcgi_pass php;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx;
        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_NAME /index.php;
}
location ~ \.php$ {
	try_files $uri @wordpress;
	fastcgi_index index.php;
	fastcgi_pass php;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx;
        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;
        fastcgi_pass 127.0.0.1:9000;
}

File: /etc/php5/conf.d/apc.ini

; configuration for php apc module
extension = apc.so
apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 512M
apc.optimization = 0
apc.num_files_hint = 2700
apc.user_entries_hint = 2700
apc.ttl = 7200
apc.user_ttl = 3600
apc.gc_ttl = 600
apc.cache_by_default = 1
apc.slam_defense = 1
apc.use_request_time = 1
apc.mmap_file_mask = /dev/zero
apc.file_update_protection = 2
apc.enable_cli = 0
apc.max_file_size = 2M
apc.stat = 1
apc.write_lock = 1
apc.report_autofilter = 0
apc.include_once_override = 0
apc.rfc1867 = 0
apc.rfc1867_prefix = "upload_"
apc.rfc1867_name = "APC_UPLOAD_PROGRESS"
apc.rfc1867_freq = 0
apc.localcache = 1
apc.localcache.size = 1350
apc.coredump_unmap = 0
apc.stat_ctime = 0

Php.ini is located at /etc/php5/php.ini use the production defaults explained in the file.

File: /etc/my.cnf
The default is very long and well documented. Copy it to my.cnf.back and use this as a starting place (have at least 1GB Ram before using this file)

[client]
port		= 3306
socket		= /var/run/mysqld/mysqld.sock
[mysqld_safe]
socket		= /var/run/mysqld/mysqld.sock
nice		= 0
[mysqld]
user		= mysql
pid-file	= /var/run/mysqld/mysqld.pid
socket		= /var/run/mysqld/mysqld.sock
port		= 3306
basedir		= /usr
datadir		= /var/lib/mysql
tmpdir		= /tmp
language	= /usr/share/mysql/english
skip-external-locking
key_buffer	= 16M
max_allowed_packet  = 16M
thread_stack	    = 192K
thread_cache_size   = 16
myisam-recover      = BACKUP
max_connections     = 100
table_cache         = 256
thread_concurrency  = 16
query_cache_limit   = 4M
query_cache_size    = 128M
general_log_file    = /var/log/mysql/mysql.log
general_log         = 1
log_slow_queries    = /var/log/mysql/mysql-slow.log
long_query_time = 2
log-queries-not-using-indexes
expire_logs_days    = 10
max_binlog_size     = 100M
[mysqldump]
quick
quote-names
max_allowed_packet   = 16M
[isamchk]
key_buffer	    = 16M
# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/

Whenever you make changes to a .conf file you need to restart the server to apply your changes.

/etc/init.d/mysql restart
/etc/init.d/nginx restart

Next Page: Installing WordPress and W3 Total Cache

Pages: 1 2 3 4 5 6

15 Responses »

  1. just checking one thing: the w3 total cache rewrite rules are only needed if using disk cache, right?

    • Yes you really only need the W3 rules for disc cache. They just add a finer level of control for the browser cache rules. I just updated the server file to show a full example of the W3 Rules using disk enhanced page cache and disk minify.

  2. Hi,

    Great post, thanks a lot for the information. From the title I got the impression, you were going into the details of multiple wp installs with one ngnix or via multiple xen installs.

    Do you have any best practices about this? Or even some up to date ressources?

    Thanks again for the great post.

  3. I’ve got a noob question about SSH keys. If I’ve installed the SSH key on the server it means that I can only login to it from my computer right? What if I don’t have my computer with me and have an emergency and need to SSH into the server?

    • Using SSH keys you will only be able to login if your computer contains a private key that matches a public key installed on the server. I prefer the added added security they provide but if you often need to access your server away from your computer then maybe a really strong password would be better for you.

      If your using password authentication you can change the ssh port to prevent a majority of the brute force attempts. Also some VPS and dedicated server hosts have a java console you can use to access your server without having to use your ssh keys.

  4. Just a note that “you are” is shortened to “you’re”, not “your”.

    Otherwise, thanks for the great write up!

  5. Hey Guys:

    Awesome tutorial — my first foray into nginx w/ wordpress.

    A couple things I got snagged on in the tutorial & the fixes:

    > When installing everything the first ‘php5-mysql’ should be ‘mysql-server’ instead since there is already a php5-mysql package a little later in the command

    > There’s a missing closing ‘}’ at the end of the file in the sites conf

    > Upon trying to install wordpress I got “Your PHP installation appears to be missing the MySQL extension which is required by WordPress.” Fixed this by editing the php.ini inside the php-fpm dir.
    >> I un-commented ‘extention=mysql.so’ as well as uncommenting ‘extention_dir = “./”‘ and changing ‘./’ to /usr/lib/php5/20090626+lfs and all was well.

    Thanks again guys! Loving nginx so far!

  6. Hi

    Great guide thank you, I have one question about file ownerships/permissions.

    Presumably everything in my site’s public folder needs to be owned by www-data and in the www-data group?

    If so, how can I then upload and/or edit files when logged in as a non-root user? Do I just need to add my user to the www-data group?

    Any advice much appreciated, I find this bit quite confusing!

  7. Any recommendations for creating init scripts to ensure all the services start automatically after a reboot?

  8. -location ~ \.php {
    +location ~ \.php$ {

    -location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    +location ~* \.(ico|css|js|gif|jpe?g|png)$ {

    -rewrite ^.*/files/(.+)$ /wp-includes/ms-files.php?file=$1 last;
    +rewrite /files/(.+)$ /wp-includes/ms-files.php?file=$1 last;

  9. Hi,

    Thanks for your tutorial, I finally get my wordpress working on LEMP stack.

    However, if any of your reader need the easy way to install wordpress, you can use https://github.com/buchin/WordPress-On-Lemper
    Which is based on this tutorial.

    It assumes you’ve installed nginx, php-fpm, and mysql, and need multipe WordPress installed on multiple domains.

Trackbacks

  1. Adding Git to your WordPress development workflow

Leave a Response