Tutorials

Bootstraping WordPress/Nginx/PHP/MySQL on a Cheap VPS with lowendscript

One constantly posted question on LowEndBox and LowEndTalk is — how to get the same setup as LowEndBox, a WordPress site that runs on cheap VPS with 80MB of memory? Instead of doing a step-by-step explanation (damn I hate writing long blog posts), I decided to wrap it into a bash script that does it all.

NginxPHP
WordPressMySQL

It is based on my previous script here, and it has been tested on both Debian 5 “Lenny” and Ubuntu 10.04 “Lucid Lynx”. Sorry no other distributions are supported. It is currently hosted at github so feel free to branch it and tune it to your heart’s desire.

Usage

Here is a quick step by step on how to set up a WordPress blog running on Nginx, PHP/FastCGI and MySQL on a low end box. Let’s assume that you are going to set up a blog on hostname “blog.example.com“.

  1. Go and buy a cheap VPS. Yes, you know where to find cheap virtual servers under $7/month, don’t you? 🙂 I am using my 6 pound/year RackVM VPS with 128MB memory to test this script, and the performance turns out to be quite good. YMMV.

    Make sure it is either a Debian or a Ubuntu distro though.

  2. After gaining root access to your VPS,
    # wget --no-check-certificate https://github.com/lowendbox/lowendscript/raw/master/setup-debian.sh
    ...
    # bash setup-debian.sh system
    ...
    # bash setup-debian.sh exim4
    ...
    # bash setup-debian.sh nginx
    ...
    # bash setup-debian.sh mysql
    ...
    # bash setup-debian.sh php
    ...
    # bash setup-debian.sh wordpress blog.example.com
    ...
    
  3. Now altering your DNS entry to point blog.example.com to your new VPS (or alter your local /etc/hosts entry). Navigate to it from your browser, and you should be presented with WordPress set up page. Fill in your blog’s name, admin’s password and contact email, and your new WordPress is online!

    Just repeat the last step (running bash setup-debian.sh wordpress <hostname>) to set up more blogs on the same box.

That’s it!

Software Stack

It’s pretty similar to my own stack that runs LowEndBox.com.

  • dropbear to replace openssh. Invoked from xinetd.
  • inetutils-syslogd to replace rsyslog.
  • exim4 to replace sendmail (if installed). Re-configured to allow Internet delivery.
  • cron
  • nginx
  • mysql. Re-configured to remove innodb support, remove query cache and reduce key buffer size.
  • php with built-in FastCGI interface. Running only 1 child process and respawn after 5,000 requests.

The lowendscript is also

  • Removing some commonly bundled applications that should not be there in the first place for a minimal distro (apache2, sendmail, bind9, samba, nscd, etc).
  • MySQL root is given a new password (which can be found in ~root/.my.cnf)
  • Installing each WordPress site under /var/www/<hostname>. It will create appropriate database, users and password for the site.

I have to say that it is still “work in progress”, but if you want to figure out the internals please check the source code of that bash script.

Memory Usage

I am testing the same script on the following 3 systems:

  • Ubuntu 10.04 64bit (RackVM)
  • Debian 5 64bit (RackVM)
  • Debian 5 32bit (VPSGuy)

They are all showing different memory usage. I am taking the snapshot of memory usage AFTER 10 page loads on the WordPress blog that has been created to ensure PHP process is fully loaded.

Ubuntu 10.04 64bit

root@ubuntu64:~# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.5  23300  1536 ?        Ss   06:58   0:00 init
root     10124  0.0  0.5  23576  1496 ?        Ss   07:34   0:00 dropbear -i
root     15742  0.0  0.7  17880  1944 pts/0    Ss   07:35   0:00 -bash
root     15754  0.0  0.4  14960  1084 pts/0    R+   07:35   0:00 ps aux
root     24290  0.0  0.3  12508   788 ?        S    07:22   0:00 /usr/sbin/syslo
root     24524  0.0  0.3  21064  1004 ?        Ss   07:22   0:00 cron
root     25689  0.0  0.3  19512   972 ?        Ss   07:22   0:00 /usr/sbin/xinet
mysql    26346  0.0  4.2  61812 11248 ?        Ssl  07:23   0:00 /usr/sbin/mysql
www-data 27920  0.0  2.6  87768  7048 ?        Ss   07:23   0:00 /usr/bin/php-cg
www-data 27922  0.5 15.0 122460 39432 ?        S    07:23   0:04 /usr/bin/php-cg
103      30162  0.0  0.3  44356  1000 ?        Ss   07:24   0:00 /usr/sbin/exim4
root     30267  0.0  0.3  28104   932 ?        Ss   07:24   0:00 nginx: master p
www-data 30268  0.0  0.8  28744  2196 ?        S    07:24   0:00 nginx: worker p

root@ubuntu64:~# free
             total       used       free     shared    buffers     cached
Mem:        262144     100500     161644          0          0          0
-/+ buffers/cache:     100500     161644
Swap:            0          0          0

Memory Usage: 98.1MB

Debian 5 64bit

debian64:~# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2  10304   740 ?        Ss   12:22   0:00 init [2]
106       1414  0.0  0.3  42708  1004 ?        Ss   12:26   0:00 /usr/sbin/exim4
www-data  1655  0.0  2.5  72028  6560 ?        Ss   12:26   0:00 /usr/bin/php-cg
www-data  1657  0.9 11.7  98500 30928 ?        S    12:26   0:03 /usr/bin/php-cg
www-data  3126  0.0  0.7  28380  1912 ?        S    12:29   0:00 nginx: worker p
root      7668  0.1  0.5  23340  1528 ?        Rs   12:31   0:00 dropbear -i
root      7831  0.0  0.6  17472  1716 pts/0    Ss   12:32   0:00 -bash
root      7836  0.0  0.3  14720   980 pts/0    R+   12:32   0:00 ps aux
root     20169  0.0  0.3  18540   936 ?        Ss   12:22   0:00 /usr/sbin/cron
root     24462  0.0  0.2  12236   732 ?        S    12:25   0:00 /usr/sbin/syslo
root     25637  0.0  0.3  19256   952 ?        Ss   12:25   0:00 /usr/sbin/xinet
root     25694  0.0  0.5  28380  1552 ?        Ss   12:25   0:00 nginx: master p
root     32427  0.0  0.2   3872   588 ?        S    12:26   0:00 /bin/sh /usr/bi
mysql    32466  0.0  3.0  50420  8124 ?        Sl   12:26   0:00 /usr/sbin/mysql
root     32468  0.0  0.2   3772   592 ?        S    12:26   0:00 logger -p daemo

debian64:~# free
             total       used       free     shared    buffers     cached
Mem:        262144      61008     201136          0          0          0
-/+ buffers/cache:      61008     201136
Swap:            0          0          0

Memory Usage: 59.6MB

Debian 5 32bit

debian32:~# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1   1980   688 ?        Ss   23:34   0:00 init [2]
root     14064  0.0  0.1   1704   512 ?        S    23:52   0:00 /bin/sh /usr/bi
mysql    14119  0.0  1.7  20056  6968 ?        Sl   23:52   0:00 /usr/sbin/mysql
root     14120  0.0  0.1   1628   536 ?        S    23:52   0:00 logger -p daemo
www-data 15968  0.0  1.2  16352  4996 ?        Ss   23:53   0:00 /usr/bin/php-cg
www-data 15971  0.5  4.7  29436 18576 ?        S    23:53   0:01 /usr/bin/php-cg
root     16040  0.0  0.3   2700  1464 ?        Rs   23:53   0:00 dropbear -i
root     16119  0.0  0.1   1880   684 ?        S    23:44   0:00 /usr/sbin/syslo
root     16133  0.0  0.3   2780  1536 pts/0    Ss   23:54   0:00 -bash
root     17728  0.0  0.2   2352   896 ?        Ss   23:45   0:00 /usr/sbin/xinet
www-data 17758  0.0  0.3   5004  1488 ?        S    23:54   0:00 nginx: worker p
root     27834  0.0  0.3   4548  1236 ?        Ss   23:47   0:00 nginx: master p
root     28146  0.0  0.2   2296   896 pts/0    R+   23:57   0:00 ps aux
102      32065  0.0  0.2   6104   900 ?        Ss   23:49   0:00 /usr/sbin/exim4

debian32:~# free
             total       used       free     shared    buffers     cached
Mem:        262144      44272     217872          0          0          0
-/+ buffers/cache:      44272     217872
Swap:            0          0          0

Memory Usage: 43.2MB

So to put that in summary form

  • Ubuntu 10.04 uses significantly more memory than Debian 5 (PHP5.3/MySQL5.1 vs. PHP5.2/MySQL5.0). So pick your distro wisely and maybe you don’t need the latest and greatest if your application does not need it.
  • 32bit also uses quite a bit less than 64bit, running exactly the same version of stack. That pretty much confirms what I posted before.

And hopefully the above script is useful for your next WordPress splog farm 😛

490 Comments

  1. David's avatar
    David

    Using Ubuntu 64bit, only using 65mb of RAM, doing nothing but running APT-GET Upgrade and Update before, and running the above script.

    free -m
    total used free shared buffers cached
    Mem: 2048 65 1982 0 0 0
    -/+ buffers/cache: 65 1982
    Swap: 0 0 0

  2. terii's avatar
    terii

    @LEB
    What was your reason starting dropbear with xinetd?

    I see your notes about “easier to configure” in your script… but someone expect things in /etc/default/ may be confused.

  3. LEA's avatar
    LowEndAdmin Author

    @terii — well that’s just my setup, because I am actually using xinetd for a few other things so instead of running dropbear in daemon mode + xinetd, I’ll just run dropbear in inetd mode, forked from xinetd.

  4. dmmcintyre3's avatar
    dmmcintyre3

    fang:/var/www# free -m
    total used free shared buffers cached
    Mem: 256 49 206 0 0 0
    -/+ buffers/cache: 49 206
    Swap: 0 0 0

    This is after a few PHP pageviews. Debian 5 64 bit. 2 PHP FCGI Children and recycling them after 50 requests.

  5. dmmcintyre3's avatar
    dmmcintyre3

    I should note though, that that is a MyBB forum not WordPress running on that server.

  6. dmmcintyre3's avatar
    dmmcintyre3

    I modified it a little to add the ability to set up a database, directory and configure nginx without installing wordpress.

    It’s located at http://freevps.tk/downloads/setup-debian.sh and the line to run make it is setup-debian.sh domain {domain}
    Obviously you replace {domain} with your domain name.

  7. yohan's avatar
    yohan

    is there an easier way to chroot sftp or ssh of dropbear?
    is the change of host key popup during sftp normal?

  8. Ebrahim's avatar
    Ebrahim

    Thanks for the benchmark. I knew 32-bit uses less memory than 64-bit, but I was curious about how big is the difference. Now I’ve got a clue about numbers. It is around 38% for your setup — not a negligible number.

  9. LEA's avatar
    LowEndAdmin Author

    Hey thanks for posting on Reddit 😛

    The difference also varies depending on your software stack, and 38% more would actually be on the low end of the differences — I have seen apps use up a lot more on x86_64.

    Some monolithic apps would require large memory foot prints. For example big MySQL installation that needs more than 2GB. Or MongoDB as it uses mmap files. Otherwise I usually just stick to 32bit on my VPS.

  10. superbarney's avatar
    superbarney

    Hi, first I would like to say that this script is awesome =)

    I have no problem running wordpress via the bash setup-debian.sh wordpress myblog.example.com. PHP seems to work fine on the wordpress blog.

    But when I tried to put info.php in /var/www. It just won’t run. It will not parse the php file at all. The source is displayed.

    I’m an nginx noobie, what do I have to do to make php work at /var/www and possibly elsewhere. Thanks.

  11. Allen's avatar
    Allen

    @superbarney:

    The problem will be that fastcgi parameterers are not passed in the main nginx configuration I supposed. Accordingly to the lowendbox script, the parameters are added to “/etc/nginx/fastcgi_php” therefore if you want php to work in your root location; You will have to do a “include /etc/nginx/fastcgi_php” in the main nginx configuration which I think should be located at “/etc/nginx/”

    Or I think a more maintainable method will be to create another virtual host (For the root directory) and add it into your main ngix configuration. This way you could easily maintain the configuration for each different domains you have on the box.

  12. Nickolai Leschov's avatar
    Nickolai Leschov

    Thanks for making our lives even more easier with this script!

    LowEndAdmin, can you please add Pure-FTPd configuration to this script?

    First thing, I wanted to try serving files from my VPS. So I installed nginx to serve files pure-ftpd for uploading files via ftp to nginx’ home directory. But alas, I couldn’t make ftp work however I tried!

  13. LEA's avatar
    LowEndAdmin Author

    @Nickolai — wouldn’t SFTP an better alternative? It should work with Dropbear or OpenSSH. More secure and less hassle with firewalls.

  14. superbarney's avatar
    superbarney

    @Allen:

    Thanks for the tip but I had a feeling that was the case and did “include /etc/nginx/fastcgi_php” for the vhost that has already been setup for root, which is /etc/nginx/sites-available/default. That is if I’m not mistaken.

    After adding that line, I get “No input file specified” as the error message on my browser. =/

    I can’t figure out why the wordpress dir is able to use php flawlessly and not root (or possibly elsewhere)

  15. LEA's avatar
    LowEndAdmin Author

    @Nickolai — SFTP is available as long as you have SSH installed (which should already be there when the VPS is setup). For a free client use WinSCP. Or you can use something like SSHFS on Linux, or Expandrive to mount the remote SFTP server as a local drive.

  16. KLIKLI's avatar
    KLIKLI

    @superbarney
    Not sure what are you actually going to do, but “No input file specified” equals to error occured at the end of PHP. You’d probablly see if the path is correct, or you may try to chmod files to 755.

  17. Allen's avatar
    Allen

    @@Nickolai

    Yup! SFTP usually comes installed as a feature with the ssh server that you are using. In this case, dropbear does support sftp protocol. You can use FileZilla (Free of course!) and while you are at the site manager, you can add in to use the sftp protocol.

    I think if you are planning to serve a couple of users using FTP. Then I think getting a separate ftp server will be more appropriate than sftp. (I dunno why, but it just seems so, haha). If you are just working alone and wishes to transfer files then sftp will works as fine.

  18. marikh's avatar
    marikh

    It would be so nice, if You update nginx version 🙂 in your pack, nginx version is 0.6, but the newest version is 0.7.

  19. KLIKLI's avatar
    KLIKLI

    @marikh – The LowEndScripts uses apt-get/dpkg for installing Nginx.

  20. LEA's avatar
    LowEndAdmin Author

    @marikh — unless you want the latest features I normally just stick to the distro default so it gets automated security updates. I still have boxes running Ubuntu 8.04 with Nginx 0.5.33. As I am not a Nginx wiz and use probably the most simple functionality it does not really matter to me…

  21. Bobby's avatar
    Bobby

    I have used your script and it works. Thanks very much. The one thing I am trying to accomplish but have been unable to do is to have the configs in one place for nginx and use sites-enabled at will.

    On another centos server, using php-fpm and nginx, I was able to setup the server with only the nginx.conf file and then adding a subdomain that works for php and wordpress install was as simple as adding another subdirectory to /usr/share/nginx/sites-enabled and installing wordpress in it.

    However, I prefer your installation, because for some reason, a wordpress plugin that is needed for me errors out in the php-fpm (php 5.3.3) but works fine in your scripted version. I have not been able to figure out why. (the centos php 5.3.3 server also seems to use more memory)

    However, the simplest thing to me would seem to be to configure the debian system to use the conf and sites enabled to do the same thing as my centos server and thus solve all my problems at once.

    My nginx.conf is as follows (I have tried to modify it to the best of my abilities)

    #######################################################################
    #
    # This is the main Nginx configuration file.  
    #
    #######################################################################
    
    #----------------------------------------------------------------------
    # Main Module - directives that cover basic functionality
    #
    #   http://wiki.nginx.org/NginxHttpMainModule
    #
    #----------------------------------------------------------------------
    
    user              www-data;
    worker_processes  2;
    
    error_log  /var/log/nginx/error.log;
    #error_log  /var/log/nginx/error.log  notice;
    #error_log  /var/log/nginx/error.log  info;
    
    pid        /var/run/nginx.pid;
    
    
    #----------------------------------------------------------------------
    # Events Module 
    #
    #   http://wiki.nginx.org/NginxHttpEventsModule
    #
    #----------------------------------------------------------------------
    
    events {
        worker_connections  1024;
    }
    
    
    #----------------------------------------------------------------------
    # HTTP Core Module
    #
    #   http://wiki.nginx.org/NginxHttpCoreModule 
    #
    #----------------------------------------------------------------------
    
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
    
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  /var/log/nginx/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
        
        # Load config files from the /etc/nginx/conf.d directory
        #include /etc/nginx/conf.d/*.conf;
        #
        # The default server
        #
        server {
            listen       80;
            server_name  www.myrootdomain.com myrootdomain.com *.myrootdomain.com;
    
            #charset koi8-r;
    
            access_log  /var/log/nginx/$host.access.log;
    
            location / {
                root   /usr/share/nginx/sites-enabled/$host;
                index  index.html index.htm index.php;
            }
    
            error_page  404              /404.html;
            location = /404.html {
                root   /usr/share/nginx/sites-enabled/$host;
            }
    
            # redirect server error pages to the static page /50x.html
            #
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   /usr/share/nginx/sites-enabled/$host;
            }
    
            # proxy the PHP scripts to Apache listening on 127.0.0.1:80
            #
            #location ~ \.php$ {
            #    proxy_pass   http://127.0.0.1;
            #}
    
            # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
            #
            location ~ \.php$ {
                root           /usr/share/nginx/sites-enabled/$host;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/sites-enabled/$host$fastcgi_script_name;
                include        fastcgi_params;
            }
    
            # deny access to .htaccess files, if Apache's document root
            # concurs with nginx's one
            #
            #location ~ /\.ht {
            #    deny  all;
            #}
        }
    	
    	
    }
    
    *****************************************
    

    This works fine on my centos php-fpm server but returns not found errors on the scripted debian.

    I really need to be able to add subdomains just by putting the subdomain.myrootdomain.com directory and files in **/sites-enabled/

    I would really appreciate some help here. I have been pulling my hair out for a week now trying to get it to work.

    • LEA's avatar
      LowEndAdmin Author

      I think it wasn’t working because the lowendscript uses unix:/var/run/www/php.sock for FastCGI, whereas your script uses TCP port localhost:9000.

      Unix domain socket is slightly faster and good enough for a single box setup.

  22. Bobby's avatar
    Bobby

    I should add that your blog is adding http:// to my www
    In the server_name line I do NOT have http, just the www . myrootdomain . com
    I point this out only to avoid confusion.

  23. Bobby's avatar
    Bobby

    So how do I make mine use the php.sock for fastcgi? I could not find anything in any of the confs your scripts created that points to that.

    Or is there something in the script that I could change to make it work? Your script really is the simplest LEMP install on the net.

    Thank you also for the rapid reply.

  24. LEA's avatar
    LowEndAdmin Author

    My script creates a file called /etc/nginx/fastcgi_php and all the PHP/WordPress sites include that…

  25. Bobby's avatar
    Bobby

    I think I fixed it. Let me try and make some adjustments and test it out further.
    I am still bleary eyed from lack of sleep, but I will try to test it out.

    • Damir-ElggFactory's avatar
      Damir-ElggFactory

      Bobby, have you figured it out? I need to change it from php.sock to TCP port as well but I can’t seem to get it working.
      What changes did you make?
      Thanks.

  26. Eric's avatar
    Eric

    Thank you for the excellent guide, however there is one significant niggling issue. Everything works on first install (Ubuntu 10.04 x86), but after I restart it, I get 502 Bad Gateway. The only way it seems to fix it is to run “bash setup-debian.sh php”, after that it works fine. I tried wiping my VPS and doing it all over again, and the same issue occurs. PHP appears to be in the startup scripts folder, but restarting the php service gives me an error that it cant access /var/www/php.pid

    It seems these changes magically get wiped (the folders too), after each restart.

    Please help me out LEA

    Thanks in advance

    • J.F.'s avatar
      J.F.

      I have the same issue.Anyone?

  27. AddictingGames88's avatar
    AddictingGames88

    Great work LowEndBoxAdmin

    I did a clean install on VPS.NET SAN 2.0 VPS with Basic Debian 5.0 (x64) and followed your steps described above. For the last step, I wasn’t sure if I should use addictinggames.com or http://www.addictinggames.com In the end I decide to use
    bash setup-debian.sh wordpress http://www.addictinggames88.com

    After completing the wordpress login info, the home page still show a welcome nginx. I followed the rewrite above, but didn’t work. I then change the wordpress/site address under general setting to http://www.addictinggames88.com and the home page was able to load.

    A VPS reboot and top show 77496k RAM used. Memory usage after 1 load of the home page is 97348k RAM used.

    I have some questions.
    1) Do I have to install VSFTPD? If I have to, how do I install?
    I need to add at least 10 WordPress Plugins, but I don’t seem to be able to do it within the admin area. It asked for the FTP info, I used the SSH info, it didn’t work.

    2) How to upgrade Nginx to 0.8.50?
    Nginx version is 0.6.32. I was asked to update to the latest v0.8.50 on one of my VPS as v0.6.32 is not safe.

    3) How to replace PHP/FastCGI with PHP/PHP-FPM? If using PHP-FPM, I also need to upgrade PHP to 5.3.3? If I have do, how do I install?
    A benchmark test show that PHP-FPM is faster than FastCGI
    http://vpsbible.com/php/php-benchmarking-phpfpm-fastcgi-spawnfcgi/

    4) How to install APC on Debian?
    I like Centos because of yum

    5) Will you do a bash script for Centos?
    For now, only Centos allow me to scale SAN 2.0 VPS without reboot.

    • Ryan's avatar
      Ryan

      You don’t like Centos because of yum. You like it because you don’t understand Linux enough to make an educated decision.

      • Erawan Arif Nugroho's avatar
        Erawan Arif Nugroho

        So, didn’t smart people started from scratch and from zero?
        Don’t tell me, that you’re born as a genius, and do nothing without learning.

        If yes, then you should teach all of the newbie here, so they can be as genius as you

  28. AddictingGames88's avatar
    AddictingGames88

    Eric,

    According to my VPS support, Nginx 0.6.32 is the problem and I was asked to upgrade to the latest version.

  29. dmmcintyre3's avatar
    dmmcintyre3

    About the wordpress asking for the ftp details, make sure it has full access to the wordpress directory and is owned by the same user who is running the php files.
    (www-data if you used this script.)

  30. AddictingGames88's avatar
    AddictingGames88

    @dmmcintyre3,

    If I use this script, FTP Username = www-data, what about the FTP Password?

  31. LEA's avatar
    LowEndAdmin Author

    Well. Real WordPress users use subversion to manage their WordPress and plugin installations so `svn switch` would do the upgrade 🙂

  32. Rolando Tinggian's avatar
    Rolando Tinggian

    Hello,

    I wanted test webserver for my vps (for learning) so I put “testblog” as hostname (No domain) but when I go to http://myip/testblog it says “404 not found”

    Do I have to adjust default.conf & testblog.conf ?

    also very much thanks for this script and blog.

  33. Minh@Minh Meo's Blog's avatar
    Minh@Minh Meo's Blog

    Hi LEA:
    Do you have any tips to protect Nginx webserver from Ddos attack? Your script is great for me, but today i get ddos and i dont know how to solve this 🙁

Leave a Reply

Before you comment
  • Please use your hosting provider's support system for service issues.
  • Comments may be held temporarily for spam review.
  • Use <pre>...</pre> for terminal output, or link to a paste service.

Your email address will not be published. Required fields are marked *