
We optimize VPS for $ 5 (512MB RAM / 1 CPU) so that the wordpress site can withstand a load of 42,735,587 hits per day
When you purchase a VPS server with 256MB or 512MB of RAM on board and only part of the processor power, using the default settings for services such as MySQL / PHP / Apache is a very bad idea. Currently, I have launched 3 sites on the cheapest tariff plan with 512MB RAM / 1 CPU. Not completely sure, but the attendance is about 5-10 thousand visitors per day. Next, I want to share instructions on how to optimize LAMP using only 512 MB and without going to swap. Typically, with this setting, 256 - 378Mb of memory is used and everything works pretty quickly.
Before starting the optimization, let's take a look at the amount of memory used. To do this, run the following command:
For. to see a list of running processes and sort them by memory usage, you need to run this command:
The first and obvious question to ask is “what services do I not need to use?”. Recently, I discovered a very convenient utility for managing services. It is called " sysv-rc-conf " and manages services using pseudo-graphics and checkboxes. Looks like this:

Do not start the X-server, turn off all unnecessary services and configure Apache, MySQL, PHP with only the basic necessary functionality.
Apache's biggest problem is the amount of RAM it uses. I will consider the following ways to speed up work and reduce RAM consumption:
Prefork is where the real magic happens. This is where we tell Apache to generate many processes. By default, a large amount is allocated, which leads to the consumption of server RAM. Make sure apache2.conf is not configured to run too many servers or has many spare ones. Below is an example:
Also, be sure to adjust the " KeepAliveTimeout " setting to 10 or 15. In my opinion, 15 seconds is too much than a small page needs to view and shorter than it takes to take a long time to view the page.
The default Apache web server loads too many unnecessary modules. You can check which modules are installed and enabled by the following command:
Below is a list of modules that are required for Wordpress to work.
You need to comment out the remaining modules to save memory. Or you can enable / disable modules through the command line. To enable, use the command:
To disable:
After the manipulations, you must definitely restart the Apache web server:
If you want to maximize performance, you definitely need to limit logging. On my server I set the level to “error”. Also, if you do not need detailed statistics, you can disable User-Agent or the http-referer logging.
I am satisfied with such settings, but you decide.
Tuning MySQL to use a small amount of RAM is quite simple.
Next, we will consider the following types of MySQL settings:
To optimize MySQL, we need to edit the /etc/mysql/my.cnf file .
Mysql provides several table storage engines. Two of them are the most popular - they are InnoDB and MyISAM . The main differences between them:
Table-level lock problems are only noticeable on very busy servers. For regular websites, MyISAM shows the best performance on cheap servers.
If you decide to use MyISAM tables, then you need to add the following lines to the my.cnf configuration file:
If you have only MyISAM tables, you can disable the InnoDB engine, thereby saving RAM by adding only one line to my.cnf:
If you used InnoDB in the past, then below I will provide you with a script that will automatically convert all InnoDB tables to MyISAM.
Below are a few parameters that can be adjusted to speed up the MySQL server.
This is one of the most important parameters affecting RAM consumption and performance that needs to be optimized. MySQL tries to put everything that is indexed in key buffer, so this option brings tremendous performance. The SQL query will be submitted directly from the RAM. I can’t say what size you should set for key buffer, because only you know how much RAM you have available.
If you make the same queries twice in a row, then the result is placed in the query cache, so mysql will not have to do the query again. If you intend to improve performance, this option can be of great benefit, but memory consumption will increase. Therefore, you need to set this parameter not too huge, but not too small, that is, as much as your website needs.
The following are three variables that affect how your query cache works:
This is an optional parameter. If you have already limited the number of apache processes, then everything is already fine. If not, and you need to process thousands of users at the same time, you need to increase the value of this parameter.
Each time you access a table, MySQL loads the table reference as one entry in the table cache. This is done for each parallel access to the table, it is really important for performance, not much for memory usage. You can constantly increase the table cache, but then you will run into the limit on the number of open files in your operating system, so keep that in mind. If the table cache is too small, then mysql will puke on you, but you do not want this.
Below is the correct my.cnf, which I optimized on my VPS with the lowest tariff plan.
I found Percona, which provides free MySQL tuning and helps you choose the best MySQL server features to achieve the best performance, as well as save time, avoid the difficult moments and risks that may arise when you configure my.cnf yourself.
MySQL stores statistics that help determine the best values to use. In addition, there are two convenient utilities that you can use to read these statistics and display them in an understandable format: tuning-primer.sh and mysqltuner.pl .
These scripts will allow you to monitor your MySQL server, and then provide a hint about the parameters that need to be configured on your server.
PHP does not use memory very intensively, so I don’t think that you need to worry much about the memory consumption of these processes, if your application does not need it, but even if optimization is necessary, there will be no significant reduction in memory consumption. But I researched and then found several settings for PHP configurations that reduce web server memory consumption.
Install PHP Cache, for example, such as Alternative PHP Cache. PHP cache will store compiled PHP scripts in such a way that they will be reused without compilation and, accordingly, will not create a load:
Below is my configured php.ini file.
Another thing that might be a good idea for a blog on a small server is to put it in front of a static HTTP cache, like Varnish. Which can really increase your scalability. Varnish configuration is a separate large article that requires a separate topic.
I put open the configuration of my web server in order to prove that you can achieve high performance even from the cheapest VPS container with 512MB RAM and 1Ghz CPU. I use Ubuntu 12.04 LTS, LAMP, Varnish, APC Cache to host 3 websites made on Wordpress (not multisite) with a traffic of 10k per day. Let's take a look at the test results from Blitz.io:

As you can see, 0.23% of users get Connection Timeout, when my website has 42,735,587 hits per day (WOW), something else can be optimized, but I enjoy working my web server. If you are tired of this manual or do not want to do it by hand, you can use services such as PuPHPet or Vagrant.
We determine the available memory and swap activity.
Before starting the optimization, let's take a look at the amount of memory used. To do this, run the following command:
$ free -m
For. to see a list of running processes and sort them by memory usage, you need to run this command:
$ ps -eo pmem,pcpu,rss,vsize,args | sort -k 1 -r | less
We configure the LAMP server to consume a small amount of RAM. Stop, disable unnecessary services
The first and obvious question to ask is “what services do I not need to use?”. Recently, I discovered a very convenient utility for managing services. It is called " sysv-rc-conf " and manages services using pseudo-graphics and checkboxes. Looks like this:

Here is a list of services that I changed.
- The Postfix . This service allows you to send and receive email messages for a domain. For these purposes, I use Google Apps to send mail and mailchimp to news subscribers. So I stopped and turned off this service.
- Bind9 . It is needed to manage the DNS records of your domain. It can be disabled, since all DNS records are stored on the host.
- SSHD . There are other implementations that use much less memory, but they do not support sftp, so I left this service unchanged.
Do not start the X-server, turn off all unnecessary services and configure Apache, MySQL, PHP with only the basic necessary functionality.
Apache
Apache's biggest problem is the amount of RAM it uses. I will consider the following ways to speed up work and reduce RAM consumption:
- Handle fewer concurrent requests
- Less loading of modules (disable unused);
- Less logging.
Configure Apache to use only the fewest running child processes
Prefork is where the real magic happens. This is where we tell Apache to generate many processes. By default, a large amount is allocated, which leads to the consumption of server RAM. Make sure apache2.conf is not configured to run too many servers or has many spare ones. Below is an example:
StartServers 1
MinSpareServers 1
MaxSpareServers 3
MaxClients 10
MaxRequestsPerChild 3000
StartServers 1
MinSpareThreads 5
MaxSpareThreads 15
ThreadLimit 25
ThreadsPerChild 5
MaxClients 25
MaxRequestsPerChild 200
Also, be sure to adjust the " KeepAliveTimeout " setting to 10 or 15. In my opinion, 15 seconds is too much than a small page needs to view and shorter than it takes to take a long time to view the page.
Download only the most necessary modules
The default Apache web server loads too many unnecessary modules. You can check which modules are installed and enabled by the following command:
# apache2ctl -M
Below is a list of modules that are required for Wordpress to work.
LoadModule dir_module modules/mod_dir.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule alias_module modules/mod_alias.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule rewrite_module modules/mod_rewrite.so
You need to comment out the remaining modules to save memory. Or you can enable / disable modules through the command line. To enable, use the command:
# a2enmod module_name
To disable:
# a2dismod module_name
After the manipulations, you must definitely restart the Apache web server:
# service apache2 restart
Reduce logging
If you want to maximize performance, you definitely need to limit logging. On my server I set the level to “error”. Also, if you do not need detailed statistics, you can disable User-Agent or the http-referer logging.
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel error
I am satisfied with such settings, but you decide.
MySQL server optimization
Tuning MySQL to use a small amount of RAM is quite simple.
Next, we will consider the following types of MySQL settings:
- Things we can turn off;
- Optimization of MySQL server parameters;
- Third-party MySQL configuration customization tools.
To optimize MySQL, we need to edit the /etc/mysql/my.cnf file .
Things we need to turn off
Mysql provides several table storage engines. Two of them are the most popular - they are InnoDB and MyISAM . The main differences between them:
- MyISAM offers table-level locks, which means that when information is written to the table, the entire table is locked and if at that moment there are more records that must be executed simultaneously in the same table, then they will have to wait until the first record is added successfully ;
- InnoDB , on the other hand, offers row-level locks, which means that when a write to a string occurs, only that single row is locked; the rest are available for recording.
Table-level lock problems are only noticeable on very busy servers. For regular websites, MyISAM shows the best performance on cheap servers.
If you decide to use MyISAM tables, then you need to add the following lines to the my.cnf configuration file:
default-storage-engine=MyISAM
default-tmp-storage-engine=MyISAM
If you have only MyISAM tables, you can disable the InnoDB engine, thereby saving RAM by adding only one line to my.cnf:
skip-innodb
If you used InnoDB in the past, then below I will provide you with a script that will automatically convert all InnoDB tables to MyISAM.
#!/bin/bash
MYSQLCMD=mysql
for db in `echo show databases | $MYSQLCMD | grep -v Database`; do
for table in `echo show tables | $MYSQLCMD $db | grep -v Tables_in_`; do
TABLE_TYPE=`echo show create table $table | $MYSQLCMD $db | sed -e's/.*ENGINE=\([[:alnum:]\]\+\)[[:space:]].*/\1/'|grep -v 'Create Table'`
if [ $TABLE_TYPE = "InnoDB" ] ; then
mysqldump $db $table > $db.$table.sql
echo "ALTER TABLE $table ENGINE = MyISAM" | $MYSQLCMD $db
fi
done
done
We optimize the parameters of the MySQL server
Below are a few parameters that can be adjusted to speed up the MySQL server.
Key buffer size
This is one of the most important parameters affecting RAM consumption and performance that needs to be optimized. MySQL tries to put everything that is indexed in key buffer, so this option brings tremendous performance. The SQL query will be submitted directly from the RAM. I can’t say what size you should set for key buffer, because only you know how much RAM you have available.
The query cache
If you make the same queries twice in a row, then the result is placed in the query cache, so mysql will not have to do the query again. If you intend to improve performance, this option can be of great benefit, but memory consumption will increase. Therefore, you need to set this parameter not too huge, but not too small, that is, as much as your website needs.
The following are three variables that affect how your query cache works:
query_cache_size
query_cache_limit
query_cache_type
Maximum Number of Connections
This is an optional parameter. If you have already limited the number of apache processes, then everything is already fine. If not, and you need to process thousands of users at the same time, you need to increase the value of this parameter.
The table cache
Each time you access a table, MySQL loads the table reference as one entry in the table cache. This is done for each parallel access to the table, it is really important for performance, not much for memory usage. You can constantly increase the table cache, but then you will run into the limit on the number of open files in your operating system, so keep that in mind. If the table cache is too small, then mysql will puke on you, but you do not want this.
Below is the correct my.cnf, which I optimized on my VPS with the lowest tariff plan.
My my.cnf
[mysqld]
port = 3306
socket = /var/lib/mysql/mysql.sock
skip-locking
key_buffer = 16K
max_allowed_packet = 1M
table_cache = 4
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
# For low memory, InnoDB should not be used so keep skip-innodb uncommented unless required
skip-innodb
# Uncomment the following if you are using InnoDB tables
#innodb_data_home_dir = /var/lib/mysql/
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /var/lib/mysql/
#innodb_log_arch_dir = /var/lib/mysql/
# You can set .._buffer_pool_size up to 50 - 80 %
# of RAM but beware of setting memory usage too high
#innodb_buffer_pool_size = 16M
#innodb_additional_mem_pool_size = 2M
# Set .._log_file_size to 25 % of buffer pool size
#innodb_log_file_size = 5M
#innodb_log_buffer_size = 8M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
# Remove the next comment character if you are not familiar with SQL
#safe-updates
[isamchk]
key_buffer = 8M
sort_buffer_size = 8M
[myisamchk]
key_buffer = 8M
sort_buffer_size = 8M
[mysqlhotcopy]
interactive-timeout
Third-party MySQL configuration wizards
I found Percona, which provides free MySQL tuning and helps you choose the best MySQL server features to achieve the best performance, as well as save time, avoid the difficult moments and risks that may arise when you configure my.cnf yourself.
MySQL server monitoring
MySQL stores statistics that help determine the best values to use. In addition, there are two convenient utilities that you can use to read these statistics and display them in an understandable format: tuning-primer.sh and mysqltuner.pl .
These scripts will allow you to monitor your MySQL server, and then provide a hint about the parameters that need to be configured on your server.
PHP optimization and caching
PHP does not use memory very intensively, so I don’t think that you need to worry much about the memory consumption of these processes, if your application does not need it, but even if optimization is necessary, there will be no significant reduction in memory consumption. But I researched and then found several settings for PHP configurations that reduce web server memory consumption.
; Limit the memory to 40M should be fine for barebones Wordpress
memory_limit = 48M
realpath_cache_ttl=300
realpath_cache_size=1M
Alternative PHP Cache
Install PHP Cache, for example, such as Alternative PHP Cache. PHP cache will store compiled PHP scripts in such a way that they will be reused without compilation and, accordingly, will not create a load:
# pecl install apc
Below is my configured php.ini file.
My php.ini
[APC]
extension=apc.so
apc.enabled=1
apc.shm_segments=1
;32M per WordPress install
apc.shm_size=128M
;Relative to the number of cached files (you may need to watch your stats for a day or two to find out a good number)
apc.num_files_hint=7000
;Relative to the size of WordPress
apc.user_entries_hint=4096
;The number of seconds a cache entry is allowed to idle in a slot before APC dumps the cache
apc.ttl=7200
apc.user_ttl=7200
apc.gc_ttl=3600
;Setting this to 0 will give you the best performance, as APC will
;not have to check the IO for changes. However, you must clear
;the APC cache to recompile already cached files. If you are still
;developing, updating your site daily in WP-ADMIN, and running W3TC
;set this to 1
apc.stat=1
;This MUST be 0, WP can have errors otherwise!
apc.include_once_override=0
;Only set to 1 while debugging
apc.enable_cli=0
;Allow 2 seconds after a file is created before it is cached to prevent users from seeing half-written/weird pages
apc.file_update_protection=2
;Leave at 2M or lower. WordPress does't have any file sizes close to 2M
apc.max_file_size=2M
;Ignore files
apc.filters = "/var/www/apc.php"
apc.cache_by_default=1
apc.use_request_time=1
apc.slam_defense=0
apc.mmap_file_mask=/var/www/temp/apc.XXXXXX
apc.stat_ctime=0
apc.canonicalize=1
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.lazy_classes=0
apc.lazy_functions=0
Static cache
Another thing that might be a good idea for a blog on a small server is to put it in front of a static HTTP cache, like Varnish. Which can really increase your scalability. Varnish configuration is a separate large article that requires a separate topic.
Conclusion
I put open the configuration of my web server in order to prove that you can achieve high performance even from the cheapest VPS container with 512MB RAM and 1Ghz CPU. I use Ubuntu 12.04 LTS, LAMP, Varnish, APC Cache to host 3 websites made on Wordpress (not multisite) with a traffic of 10k per day. Let's take a look at the test results from Blitz.io:

As you can see, 0.23% of users get Connection Timeout, when my website has 42,735,587 hits per day (WOW), something else can be optimized, but I enjoy working my web server. If you are tired of this manual or do not want to do it by hand, you can use services such as PuPHPet or Vagrant.