This guide is designed to provide some pointers for optimizing your WordPress installation and your hosting environment for good performance. It also provides some basic testing data and a quick guide to installing WordPress on Debian Etch.
The level of performance tweaking available to you depends largely on the level of access you have to your hosting environment. Those folks with dedicated servers (or VPS servers) where you have root shell access can take advantage of this powerful access to tweak many of the layers in your WordPress installation. Those folks on heavily shared virtual hosting without shell access have a more limited set of options available to be tweaked, but can still optimize their WordPress deployments for performance.
This document cannot take into consideration every possible hosting option, so we'll be concentrating on the most common and the most important tips and techniques.
It is worth noting that performance has 2 related but separate components to it.
The time taken for your server(s) to fulfill a single request on your site. This affects your user experience even when your site is not very busy.
The number of simultaneous requests that your server(s) can handle before becoming overloaded. This starts to affect user experience once your site starts to serve a large number of users at the same time.
Many of the performance tweaks in this guide have an impact on both speed and capacity, but some are geared more towards one or the other.
Caching has a very high Return On Investment (ROI) for performance improvements. The quickest route to the biggest performance improvements is to use caching on every delivery layer possible. Taking the output generated by a single operation and serving it multiple times without re-generating the output it is the essence of caching and improves both speed and capacity in most cases. The WordPress and the LAMP stack in general lend themselves to caching in many ways.
Let's look at some of the caching levels available and when to use them.
There are quite a few caching plugins available for WordPress. Some of these are mature projects and in use on large installations around the world.
Homepage: http://wordpress.org/extend/plugins/wp-super-cache/
WP Super Cache is a very popular WP plugin which generates static HTML files from the dynamic PHP pages on your WordPress site. It will usually increase the capacity of your server by an order of magnitude because each page load requires much less effort on the part of the web server. It should also increase speed since it usually takes a lot less time to serve a static HTML file than to wait for the PHP interpreter to generate the output. Look at "Lock Down Mode" in this plugin's settings for a solution if you think you are about to be bombarded with a traffic spike.
This is a straight forward plugin installation, and for this reason should be usable on shared hosting environments where you do not have shell access. The most up to date guide is found here: http://wordpress.org/extend/plugins/wp-super-cache/installation/.
In general, WP Super Cache only serves static HTML to user's who have not commented on, or are not logged into, your blog. For most sites this is the massive majority of your visitors so this plugin should make a big performance difference as soon as you start using it.
Homepage: http://wordpress.org/extend/plugins/batcache/
Batcache provides high performance caching based on Memcache and is suitable for deployments where you have more than one web server serving your WordPress blog/site which should share a common cache, or you choose not to (or cannot) use something like WP Super Cache.
Setting up Batcache requires that you have access to install and configure Memcached on a server where your WordPress application(s) can access it. You'll also need to make sure your PHP installation has the memcache client library installed.
The most up to date guide is found here: http://wordpress.org/extend/plugins/batcache/installation/
Similar to WP Super Cache, Batcache will try to serve uncached content to users who have logged into, or commented on your blog, so only those visitors who have not done so will get served the lightweight cached content. But again, this should be the massive majority of your visitors.
A PHP opcode cache takes the compiled script output from the PHP interpreter and caches this in memory (or on disk). This means that the PHP interpreter can simply deliver the cached output multiple times instead of regenerating it each time, and in general this delivers vast performance improvements in both speed and capacity.
Homepage: http://pecl.php.net/package/APC
Tuning APC Notes: http://tekrat.com/talks_files/phpworks2007/apc@facebook.pdf
APC has been shown time and again to be a reliable PHP opcode cache with good performance and is quite simple to install.
On Debian Etch, the following procedure will install APC:
sudo apt-get install php-pear sudo pecl install apc
Then add the following lines to your php.ini file (/etc/php5/apache2/php.ini on Debian Etch with Apache2 and PHP5 installed)
extension_dir=/usr/lib/php5/20060613+lfs extension=apc.so apc.enabled = 1 apc.shm_segments = 1 apc.shm_size = 32 apc.filters=wp-cache-config
The above configures a single 32MB shared memory APC cache (you may have more or less memory to use for this on your server), and disables APC on any paths with "wp-cache-config" in them, since this has been known to cause issues with some WordPress cache plugins.
Then restart Apache:
sudo /etc/init.d/apache2 reload
Now check your phpinfo page to make sure there is now an APC section. If there is, you now have APC enabled. If there is not, check your Apache error log for reasons why not. APC distributes a very useful management interface in a file called apc.php. You can find it on Debian Etch with:
sudo updatedb locate apc.php
You should keep this file installed somewhere (behind an Apache Authentication page preferably) and keep an eye on your cache and memory usage statistics and tune APC according to your use over time.
Manual for MySQL 5.0: http://dev.mysql.com/doc/refman/5.0/en/query-cache-configuration.html
Manual for MySQL 4.1: http://dev.mysql.com/doc/refman/4.1/en/query-cache-configuration.html
Enabling the query cache in MySQL should reduce the load on your database server, by allowing MySQL to avoid having to run the same query multiple times. The factor by which it is reduced varies widely depending on what queries you are running and how often your data is changed. This is a very simple feature to enable (is generally enabled by default) and so it makes sense to use it. The 3 most important settings for the query cache should go into your MySQL configuration file (my.cnf on most Unices).
query-cache-type = 1 #0=off, 1=on, 2=on demand query_cache_size = 20M #total amount of memory to use for the cache, depending on the amount of memory your server has query_cache_limit = 1M #the max size of any one query result which will be cached, prevents a single massive query filling up the cache
You can check if your MySQL server has the query cache enabled by running the following commands:
mysql -p mysql> show variables like '%query_cache%'; +------------------------------+----------+ | Variable_name | Value | +------------------------------+----------+ | have_query_cache | YES | | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 16777216 | | query_cache_type | ON | | query_cache_wlock_invalidate | OFF | +------------------------------+----------+
Apache Settings for Optimal Performance: http://www.stdlib.net/~colmmacc/Apachecon-EU2005/scaling-apache-presentation.pdf
Most WordPress users run Apache, so lets take a look at how to tune Apache to maximize your server resources.
Apache2 has various modules (called MPMs) which implement much of the core web server functionality. You need to choose one and only one. They differ from each other in terms of threading model, performance and configuration options. Since WordPress requires PHP, and since PHP depends on some libraries which have been known to not be thread safe on Linux/UNIX variants, the PHP developers suggest sticking with the default prefork (non threaded) MPM on Linux/UNIX when using mod_php (see below on mod_php vs FastCGI). It's worth noting that PHP distributions for Windows are considered thread safe, so the Apache MPM on Windows (winnt) implements a multithreaded model.
Tuning Apache to your environment is critical to ensure optimal performance. Generally this means understanding the memory usage on your servers properly. The most important tuning parameters for the prefork MPM are:
MaxClients:
Maximum number of Apache processes which will be forked to service incoming requests. You can use this formula to work out the best number for this settingt:
(Server Total RAM - (RAM used by OS + RAM used by other running Apps) / RSS Size of Apache Binary)
For Example: Running the follwing on Debian Etch will show you the Resident Set Size (RSS - which is the portion of a processes memory which is in RAM) of your Apache processes.
ps -ylC apache2 --sort:rss
If your server has 1024MB of RAM and after booting the OS, you have 768MB free. If after starting up MySQL you have 700MB free and after starting up PureFTP you have 650MB free. Your Apache process RSS size is 17MB. That would mean you can run 38 Apache processes before you run out of RAM and begin to swap (your web server should never swap). Set MaxClients to 38 in this case.
MaxRequestsPerChild:
After this number of requests, a child process is killed and a new one forked to replace it. This ensures that any accidental memory leakage on the part of the Apache process cannot get out of hand. Setting this to something other than 0 means that Apache processes don't live forever.
EnableMMAP:
Set this to "On" if you are using Linux and your web server Document Root is not on an NFS/network share. This allows Apache to use Memory mapping to read files and usually improves performance.
EnableSendfile:
Set this to "On" if you are using Linux and if your web server Document Root is not on an NFS/network share, and you are not using IPv6. This allows Apache to use the kernel's sendfile support to improve performance on file read and write operations.
HostnameLookups:
Set this to "Off" otherwise Apache will do DNS lookups on client IPs for logging purposes and this is slow.
AllowOverride:
Setting this to "None" will mean higher performance since Apache won't look for .htaccess files in each directory, HOWEVER many WordPress features (like nice URLs) require you to use .htaccess files so we need to compromise here and set this to "All" in most cases.
WordPress PHP5 Support: http://ma.tt/2007/07/on-php, http://boren.nu/archives/2008/01/30/php-43-required-by-wp-25/ /
An oft debated topic is the performance of PHP5 vs PHP4. There have been many tests done here, and a few different results have been reached. That said, WordPress does NOT require PHP5 and may not for some time, and some people have experienced plugin or theme compatibility issues when running WordPress under PHP5 (WordPress core should operate just fine under PHP5).
How you integrate PHP into Apache has performance implications. The two most popular options are:
The PHP interpreter and all it's linked in libraries are compiled into a loadable Apache Module (mod_php) and this module is loaded into every running Apache process at startup time. This is generally the simplest way to run PHP and is supported by most hosting environments. It also introduces memory overhead because of the monolithic Apache processes which you have running, and because the PHP interpreter is included in the web server binary which services non PHP files.
An alternative method of using PHP is to have the PHP interpreter running external to the Apache process and to use the FastCGI API to interface between the web server (mod_fastcgi) and the PHP interpreter. The advantages of this method is that you don't load the full PHP stack into Apache, you can call upon PHP only when your web server needs to run a PHP script (not images, or HTML/CSS etc) and you also get security benefits of running PHP as a user other than the web server user. This setup is slightly more advanced, and usually requires custom compilation of PHP. One other possible advantage here is that you may be able to run the threaded Apache worker MPM when using FastCGI, since technically any non threadsafe PHP libraries are not being run inside Apache, but inside an external process.
There are a few web servers out there which deliver performance over and above that which is achievable with Apache. The configuration of these alternative setups is sometimes not as simple and readily documented as the configuration options with Apache. Some of alternative webservers which are known to work for WordPress installations include:
Nginx: http://wiki.codemongers.com/Main, http://elasticdog.com/2008/02/howto-install-wordpress-on-nginx/
Lighty: http://www.lighttpd.net/
In the future this document will be expanded with an Nginx/Lighty and WordPress installation guide.
Once you reach a volume of traffic which exceeds the capacity of a single server you start to require more than one web server. You can easily achive this setup by using any number of available load balancer software packages. Some of the more popular choices include:
Nginx: http://wiki.codemongers.com/NginxLoadBalanceExample
Perlbal: http://www.danga.com/perlbal/
Pound: http://www.apsis.ch/pound/
HAProxy: http://haproxy.1wt.eu/
Homepage: http://codex.wordpress.org/HyperDB
Of course, it's not only your Web Severs which will eventually reach the capacity threshold of a single server. Your database server will eventually become a bottleneck and you'll need more than one. Thats where HyperDB comes in. This WordPress component allows you to use multiple database servers, which can be replicated.
A Content Delivery Network is a third party provider which caches your site, or parts of your site (for example: your images only) and then serves the cached content to your visitors. The goal of this arrangement is to offload requests which would normally hit your server to another server on the CDN's network. This means less requests hit your servers (called the "origin" in CDN parlance), less bandwidth is required on your network, and (usually) faster delivery of content due to the CDN's optimized routing and infrastructure. Some CDNs have extra magic built into their system to deliver content to your visitors from a server geographically closest to the user's location. CDNs generally cost money. Sometimes lots of it, so this is really the realm of the highly popular sites only.
Some Commercial CDNs: http://www.LimelightNetworks.com, http://www.pantherexpress.net, http://www.mirror-image.com
Homepage: http://wordpress.org/extend/plugins/akismet/
Using the Akismet WordPress plugin will prevent spam comments and pingbacks/trackbacks from overwhelming your servers should your blogs/site become the target for massive numbers of these.
Here is a table of some very basic testing done with ApacheBench (ab) on a WordPress installation on a Debian Etch VMWare instance. This was by no means a scientific test, merely a quick look at how tuning some components can make relatively large differences. These numbers are mainly concerned with speed, rather than capacity, since measuring capacity would require slightly more involved testing.
WordPress Version | Apache2 Version | PHP Version | MySQL Version | MySQL Query Cache | APC ON | APC Memory | Reqs / Sec | Mean Sec/Req | Data Transfer Speed |
---|---|---|---|---|---|---|---|---|---|
2.6.2 | 2.2.3 | PHP5 | 5.0.32 | OFF | OFF | - | 6.1 r/s | 1610 ms/req | 27Kbps |
2.6.2 | 2.2.3 | PHP5 | 5.0.32 | ON | OFF | - | 6.6 r/s | 1515 ms/req | 29Kbps |
2.6.2 | 2.2.3 | PHP5 | 5.0.32 | ON | ON | 32MB | 36.63 r/s | 263 ms/req | 164Kbps |
2.6.2 | 2.2.3 | PHP5 | 5.0.32 | ON | ON | 64MB | 38.35 r/s | 260 ms/req | 171Kbps |
2.6.2 | 2.2.3 | PHP4 | 5.0.32 | OFF | OFF | - | 7.0 r/s | 1413 ms/req | 31Kbps |
2.6.2 | 2.2.3 | PHP4 | 5.0.32 | ON | OFF | - | 7.2 r/s | 1384 ms/req | 32Kbps |
2.6.2 | 2.2.3 | PHP4 | 5.0.32 | ON | ON | 32MB | 29.2 r/s | 342 ms/req | 130Kbps |
2.6.2 | 2.2.3 | PHP4 | 5.0.32 | ON | ON | 64MB | 28.55 r/s | 1610 ms/req | 131Kbps |
The interesting things to note from these multiple runs are:
The fastest configuration was: Apache2, MySQL5 (with query cache enabled), APC with 64MB of memory and PHP5. This was 29% faster than the equivalent configuration with PHP4.
http://joseph.randomnetworks.com/archives/2008/09/01/slides-from-wordpress-performance-scalability/