VPS Configuration for Apache, MySQL, PHP
After having tried out SliceHost and Amazon EC2 micro instance, Linode is the only VPS that worked out well for my needs even though EC2 Micro offers 768MB RAM versus Linode’s 512MB. The main bottleneck I faced was with WordPress sites which are taxing on CPU and disk IO. They also leak memory.
After trying out various LAMP configurations the one that worked best in the end was generated using Linode’s own configuration script presented here with minor changes. This script is for the default install which includes Apache 2 + PHP Prefork + MySQL 5 and modifies the config files directly (my.cnf, php.ini, apache2.conf).
The script also makes backups of the config file (though if you run it twice, the old backup will be overwritten so make your own backups just in case).
The script sets MaxRequestsPerChild to 500 which is helpful to mitigate WordPress/PHP memory leaks since this causes Apache to recycle worker processes every so often. You should experiment with higher settings here until you encounter memory leak issues.
#!/bin/bash # # StackScript Bash Library # # Copyright (c) 2010 Linode LLC / Christopher S. Aker# All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, this # list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # * Neither the name of Linode LLC nor the names of its contributors may be # used to endorse or promote products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. function mysql_tune { # Tunes MySQL's memory usage to utilize the percentage of memory you specify, defaulting to 40% # $1 - the percent of system memory to allocate towards MySQL MYSQLCONF=/etc/mysql/my.cnf cp $MYSQLCONF $MYSQLCONF.backup if [ ! -n "$1" ]; then PERCENT=40 else PERCENT="$1" fi # sed -i -e 's/^#skip-innodb/skip-innodb/' $MYSQLCONF # disable innodb - saves about 100M MEM=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) # how much memory in MB this system has MYMEM=$((MEM*PERCENT/100)) # how much memory we'd like to tune mysql with MYMEMCHUNKS=$((MYMEM/4)) # how many 4MB chunks we have to play with # mysql config options we want to set to the percentages in the second list, respectively OPTLIST=(key_buffer sort_buffer_size read_buffer_size read_rnd_buffer_size myisam_sort_buffer_size query_cache_size) DISTLIST=(75 1 1 1 5 15) for opt in ${OPTLIST[@]}; do sed -i -e "/\[mysqld\]/,/\[.*\]/s/^$opt/#$opt/" $MYSQLCONF done for i in ${!OPTLIST[*]}; do val=$(echo | awk "{print int((${DISTLIST[$i]} * $MYMEMCHUNKS/100))*4}") if [ $val -lt 4 ] then val=4 fi config="${config}\n${OPTLIST[$i]} = ${val}M" done sed -i -e "s/\(\[mysqld\]\)/\1\n$config\n/" $MYSQLCONF } function apache_tune { # Tunes Apache's memory to use the percentage of RAM you specify, defaulting to 40% # $1 - the percent of system memory to allocate towards Apache APACHECONF=/etc/apache2/apache2.conf cp $APACHECONF $APACHECONF.backup if [ ! -n "$1" ]; then PERCENT=40 else PERCENT="$1" fi PERPROCMEM=10 # the amount of memory in MB each apache process is likely to utilize MEM=$(grep MemTotal /proc/meminfo | awk '{ print int($2/1024) }') # how much memory in MB this system has MAXCLIENTS=$((MEM*PERCENT/100/PERPROCMEM)) # calculate MaxClients MAXCLIENTS=${MAXCLIENTS/.*} # cast to an integer MAXREQUESTSPERCHILD=500 KEEPALIVETIMEOUT=2 sed -i -e "s/\(^[ \t]*MaxClients[ \t]*\)[0-9]*/\1$MAXCLIENTS/" $APACHECONF sed -i -e "s/\(^[ \t]*MaxRequestsPerChild[ \t]*\)[0-9]*/\1$MAXREQUESTSPERCHILD/" $APACHECONF sed -i -e "s/\(^[ \t]*KeepAliveTimeout[ \t]*\)[0-9]*/\1$KEEPALIVETIMEOUT/" $APACHECONF } function php_tune { PHPINI=/etc/php5/apache2/php.ini # Tunes PHP to utilize up to 32M per process sed -i'-orig' 's/memory_limit = [0-9]\+M/memory_limit = 32M/' $PHPINI } mysql_tune 40 apache_tune 40 php_tune /etc/init.d/apache2 reload /etc/init.d/mysql reload
By default the script allocates 40% memory each to MySQL and Apache. You can change two lines near the bottom to read mysql_tune 50
and apache_tune 30
for example, if you want to allow MySQL to use 50% memory and Apache 30%. However, make sure the Apache + MySQL combined don’t take more than 80% otherwise the rest of the system won’t have any RAM left to work with.
Thanks for the script there, one issue with it.
MAXREQUESTSPERCHILD = 500
KEEPALIVETIMEOUT = 2
They didn’t get set for me. They were set blank, had to go in and edit them. I didn’t fix the script just did it after the fact. Anyways, good post.
@srw2d, thanks for pointing that out, I have fixed the script. There should have been no space when declaring those variables.
Really nice script,. This will make Apache + MySQL configuration a charm,.
Btw what do you suggest in terms of memory:
40%-40% MySQL and Apache, or
50%-30% MySQL and Apache ??
40-40 has been working quite well for me but it really depends on your application profile.
Worked great for me. I’m getting much better performance, and my VPS uses almost all the ram without using any swap. 40/40 split.
Nice post. Thanks:-)