One thing I hated about WebHostingTalk is how much bad advice the so-called “professionals” are giving out to the world. Some poor college student asked in the VPS forums whether he is able to run 18 static HTML sites on VPSLink.com Link-1 plan (64MB RAM, 2.5GB storage & 100GB/month data), and the typical responses are:
So, just trying to prove the point that yes, 64MB is more than enough to host 18 static sites, I decided to add a Link-1 Xen to my account and document the process. Btw, thanks to Dan @ VPSLink for getting my billing issue resolved :) You can get 10% recursive discount here, or 66% off for the first 3 months here.
First of all — get Lighttpd installed.
Next, we need to get our websites up there and point Lighttpd to them. It’s a good idea to put the web sites in an organised structure inside the file system. I usually just place them this way:
/html, with resolved to your VPS’s IP address, and you will have your static websites over there in no time. You do not even need to tell Lighttpd to reload, as mod_simple_vhost automatically maps the hostname to appropriate file name. Repeat it 18 times and problem solved!
At 1 single testing site with no traffic, Lighttpd sits at around 1.5MB RSS, although I doubt it would increase significantly when you increase the number of sites or the traffic. Lighttpd and Nginx are single-threaded poll-based asynchronised web servers so for static file serving, the bottle-neck would be disk/network IO rather than amount of memory or CPU performance.
There are still lots of memory left. Maybe we can have some fun.
To run WordPress from your static-file serving Lighttpd, you need a few more packages — namely MySQL and PHP in CGI/FastCGI mode.
We then need to configure lighttpd to handle PHP files.
My plan:
It is also relatively easy to set up pretty permalinks for WordPress on Lighttpd. In our example,
MySQL is by far the biggest offender, and I have talked about how to reduce MySQL memory usage here. If you are just running simple CMS, InnoDB is probably not required — it uses more memory and a lot heavier on IO as well. We can simply use the LxAdmin’s mysql.cnf which I linked on the other blog post to get the bare-minimum MySQL running.
Next — Portmap and FAM got installed when Lighttpd was first installed. Lighttpd does not really need FAM. It’s used for stat cache to reduce seeks, but can live without. Not that I have noticed any performance difference anyway for small traffic anyway. Having both of them removed from the process list would give us extra 750KB.
That’s probably it! Vixie cron can be replaced by a light weight DCRON but I can’t seem to be able find it in Debian’s repository. Exim4 is probably one of the most light weight mail daemon you can have, but then again you might want to question — “do I need a mail daemon running”? You can probably bring it down, and just run
You can also use PDKSH to replace BASH as interactive shell to loose some weight.
Here’s the end result:
One thing about root access though — in all my examples above I used root account and never bothered to use a normal user account. It is bad from security aspect so don’t do it. Or at least don’t tell anyone that you use nothing but root :)
“I do not believe you can host 18 websites on 64MB of RAM. I’d bump that up to at least 128 or 256.” –nexbyte(Well, there are some more optimistic comments but I mainly list out those “with things to sell”)
“I really wouldn’t advise anything lower than 265MB RAM for website hosting.” –RikeMedia
So, just trying to prove the point that yes, 64MB is more than enough to host 18 static sites, I decided to add a Link-1 Xen to my account and document the process. Btw, thanks to Dan @ VPSLink for getting my billing issue resolved :) You can get 10% recursive discount here, or 66% off for the first 3 months here.
Setting Up the VPS
After my order has been provisioned, I re-image the server with a Debian 5 “Lenny” image. I normally pick Debian or Ubuntu becauseapt-get
uses much less memory than RedHat/Fedora’s equivalent, and it’s also my personal preference. I named my new VPS “endor” as I usually just name my boxes after Star Wars systems. Re-imaging a VPS is pretty fast — 2 minutes later I have my root password sent to my email address so I can ssh in to set up the new system.$ ssh root@endor root@endor's password: Linux 66671 2.6.18-53.1.13.el5xen #1 SMP Tue Feb 12 14:04:18 EST 2008 i686 endor:~# free total used free shared buffers cached Mem: 65704 64008 1696 0 5616 44100 -/+ buffers/cache: 14292 51412 Swap: 131064 0 131064 endor:~# cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 15 model name : Intel(R) Core(TM)2 Duo CPU E4500 @ 2.20GHz stepping : 13 cpu MHz : 2194.496 cache size : 2048 KB fdiv_bug : no hlt_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 10 wp : yes flags : fpu tsc msr pae mce cx8 apic mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc up pni monitor ds_cpl est tm2 cx16 xtpr lahf_lm bogomips : 5558.81Plenty of free memory and a single core of C2Duo E4500 — although not a high-end Xeon CPU, but should be more than sufficient to do what we need it to do. The next thing I want to do is to make sure every package is up to date.
endor:~# apt-get update && apt-get upgrade Get:1 http://debrepo.mirror.vpslink.com lenny Release.gpg [386B] Get:2 http://debrepo.mirror.vpslink.com lenny Release [63.2kB] Get:3 http://debrepo.mirror.vpslink.com lenny/main Packages [5295kB] Get:4 http://security.debian.org lenny/updates Release.gpg [197B] Get:5 http://security.debian.org lenny/updates Release [40.8kB] Get:6 http://debrepo.mirror.vpslink.com lenny/contrib Packages [76.1kB] Ign http://security.debian.org lenny/updates/main Packages/DiffIndex Get:7 http://security.debian.org lenny/updates/contrib Packages [14B] Get:8 http://security.debian.org lenny/updates/main Packages [50.6kB] Fetched 5526kB in 4s (1330kB/s) Reading package lists... Done ...
Setting Up Web Server
Okay. The 64MB VPS is now up and running. What should we do next? Installing a web server of course, so we can start serving our static pages! Which web server? Definitely not Apache as it would be a waste of valuable memory here. Again my personal favourite is Nginx (pronounces Engine X), which currently powers LowEndBox.com. However, in this exercise I will go for Lighttpd because I found it easier to set up for abitary sites.First of all — get Lighttpd installed.
endor:~# apt-get install lighttpd Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: ... Setting up libterm-readkey-perl (2.30-4) ... Setting up libterm-readline-perl-perl (1.0302-1) ... Setting up lighttpd (1.4.19-5) ... Starting web server: lighttpd.
endor:~# ps -u www-data u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND www-data 1690 0.0 1.5 5416 1008 ? S 07:17 0:00 /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.confPlain vanilla stripped down and un-configured 32 bit Lighttpd sits around 1MB RSS — not bad.
Next, we need to get our websites up there and point Lighttpd to them. It’s a good idea to put the web sites in an organised structure inside the file system. I usually just place them this way:
/var/www/
/html
/var/www/www.example.com/html/testing.html
. Unfortunately I do not have 18 static sites. For testing purpose I am only going to display a very basic HTML page at http://test.lowendbox.com/.endor:~# mkdir -p /var/www/test.lowendbox.com/html endor:~# echo 'So now our “website” is ready — how does Lighttpd, our webserver, knows where to find the files corresponding to the website? That’s where Lighttpd’s mod_simple_vhost comes in handy.Low End Box Rocks!
' > /var/www/test.lowendbox.com/html/index.html
endor:~# lighttpd-enable-mod simple-vhost Available modules: auth cgi fastcgi proxy rrdtool simple-vhost ssi ssl status userdir Already enabled modules: Enabling simple-vhost: ok Run /etc/init.d/lighttpd force-reload to enable changes endor:~# /etc/init.d/lighttpd force-reload Stopping web server: lighttpd. Starting web server: lighttpd.Now navigate to test.lowendbox.com (which already has an A record to my new VPS’s IP address) — here we have it! Low End Box Rocks!!!
Prerequisite:
You must be already familiar with DNS and know how to create records to point to IP addresses. For free DNS hosting I recommend EveryDNS, which has also been hosting LowEndBox’s domain.
You can now basically just dump static files at /var/www/You must be already familiar with DNS and know how to create records to point to IP addresses. For free DNS hosting I recommend EveryDNS, which has also been hosting LowEndBox’s domain.
At 1 single testing site with no traffic, Lighttpd sits at around 1.5MB RSS, although I doubt it would increase significantly when you increase the number of sites or the traffic. Lighttpd and Nginx are single-threaded poll-based asynchronised web servers so for static file serving, the bottle-neck would be disk/network IO rather than amount of memory or CPU performance.
There are still lots of memory left. Maybe we can have some fun.
Installing WordPress
So you think, “hey Low End Box rocks and it runs on WordPress. So maybe I will have that installed as well!” Wow. But WordPress is a content management system for creating dynamic websites! It simply cannot be possible on a 64MB VPS, the WHT crowd says! Grrr!! Let’s give it a try.To run WordPress from your static-file serving Lighttpd, you need a few more packages — namely MySQL and PHP in CGI/FastCGI mode.
endor:~# apt-get install mysql-server php5-cgi php5-mysql Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: ... Creating config file /etc/php5/cgi/php.ini with new version Setting up php5-mysql (5.2.6.dfsg.1-1+lenny2) ... Setting up sgml-base (1.26) ... Setting up xml-core (0.12) ... Setting up mailx (1:20071201-3) ...I know it installs whole lot of other junks but don’t worry — we will live with them first and will try to optimise later. It also requires you to set up the root password for MySQL server, and I conveniently chose the most obscured password in this exercise — “root” (yes, don’t use that because I am already using it as my root password :)
We then need to configure lighttpd to handle PHP files.
endor:~# cat > /etc/lighttpd/conf-enabled/10-cgi-php.conf server.modules += ("mod_cgi") cgi.assign = (".php" => "/usr/bin/php5-cgi")^D endor:~# /etc/init.d/lighttpd force-reload Stopping web server: lighttpd. Starting web server: lighttpd.Done! It should be able to serve PHP files. Just to test it out:
endor:~# echo '' > /var/www/test.lowendbox.com/html/phpinfo.phpNow navigate to http://test.lowendbox.com/phpinfo.php — you should be able to see the output of
phpinfo()
function. What we are going to do next is to set up a WordPress blog under http://test.lowendbox.com/blog/. WordPress.org already provides a great tutorial on installing WordPress, but let’s do it step by step on the command prompt.My plan:
- Create database “test_blog”
- Download the latest WordPress and unzip to test.lowendbox.com/blog
- Set up configuration file and run the WordPress install
- Update Lighttpd to provide clean URL, aka Pretty Permalinks.
endor:~# mysqladmin -uroot -proot create test_blog endor:~# wget http://wordpress.org/latest.tar.gz Resolving wordpress.org... 72.233.56.138, 72.233.56.139 Connecting to wordpress.org|72.233.56.138|:80... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/x-gzip] Saving to: `latest.tar.gz' ... 2009-03-17 13:03:15 (1.01 MB/s) - `latest.tar.gz' saved [1624416] endor:~# tar zxf latest.tar.gz -C /var/www/test.lowendbox.com/html endor:~# cd /var/www/test.lowendbox.com/html endor:/var/www/test.lowendbox.com/html# mv wordpress blog endor:/var/www/test.lowendbox.com/html# mv blog/wp-config-sample.php blog/wp-config.php endor:/var/www/test.lowendbox.com/html# vi blog/wp-config.phpWhen you are editing WordPress’ configuration file, set
DB_NAME
to “test_blog”, DB_USER
and DB_PASSWORD
to “root” for something quick, dirty and potentially insecure. Here is one final step — navigate to http://test.lowendbox.com/blog/, and WordPress will guide you through the rest of setup.It is also relatively easy to set up pretty permalinks for WordPress on Lighttpd. In our example,
endor:~# cat > /etc/lighttpd/conf-enabled/lowendbox.conf $HTTP["host"] == "test.lowendbox.com" { $HTTP["url"] =~ "^/blog/" { server.error-handler-404 = "/blog/index.php" } }^D endor:~# /etc/init.d/lighttpd force-reload Stopping web server: lighttpd. Starting web server: lighttpd.That’s it! You can now go into WordPress to configure the desirable Permalink Structure. Do note that the current WordPress dashboard page is very resource intensive, as it fetches development blog, other WP news, incoming links, etc from various sources, concurrently on separate PHP CGI processes. There might be plugins to turn off this server-killing behavior (or just use older version of WordPress like 2.0.x which is still maintained). Likewise some WP caching plugin can be very useful in reducing the load. Google them and you shall find.
Optimisation — Squeeze More Memory!
So now we have a Debian 5 web server box that can handle lots of static sites + a few WordPress blogs, and it fits “fine” on a 64MB Xen VPS. Let’s see what processes are running:endor:~# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ... root 325 0.0 0.4 2032 292 ? S<s 04:25 0:00 udevd --daem root 879 0.0 0.4 2788 304 ? Ss Mar17 0:00 /bin/bash -- root 1216 0.0 0.0 0 0 ? S Mar17 0:00 [pdflush] root 1649 0.0 0.2 3144 188 ? Ss Mar17 0:00 /usr/sbin/famd root 6427 0.0 4.4 8024 2928 ? Ss Mar17 0:00 sshd: root@pts/ root 6429 0.0 2.3 2804 1564 pts/0 Ss Mar17 0:00 -bash root 6441 0.0 1.8 33092 1200 ? Sl Mar17 0:00 /usr/sbin/rsysl root 6453 0.0 1.4 5284 976 ? Ss Mar17 0:00 /usr/sbin/sshd root 6470 0.0 1.3 2048 896 ? Ss Mar17 0:00 /usr/sbin/cron daemon 6482 0.0 0.8 1772 560 ? Ss Mar17 0:00 /sbin/portmap www-data 6510 0.0 2.6 5848 1736 ? S Mar17 0:00 /usr/sbin/light root 6572 0.0 1.7 2488 1156 pts/0 S Mar17 0:00 /bin/sh /usr/bi mysql 6611 0.0 26.2 143652 17228 pts/0 Sl Mar17 0:00 /usr/sbin/mysql root 6613 0.0 0.8 1636 536 pts/0 S Mar17 0:00 logger -p daemo 103 6973 0.0 1.3 6112 908 ? Ss Mar17 0:00 /usr/sbin/exim4 root 6986 0.0 1.3 2308 908 pts/0 R+ 00:01 0:00 ps aux endor:~# free total used free shared buffers cached Mem: 65704 51424 14280 0 936 22588 -/+ buffers/cache: 27900 37804 Swap: 131064 864 130200Note that it’s an idle box. The swap is slightly used and at 37MB free it is actually not too bad. Let’s try to squeeze a little bit more memory out from the factory setup.
MySQL is by far the biggest offender, and I have talked about how to reduce MySQL memory usage here. If you are just running simple CMS, InnoDB is probably not required — it uses more memory and a lot heavier on IO as well. We can simply use the LxAdmin’s mysql.cnf which I linked on the other blog post to get the bare-minimum MySQL running.
endor:~# cat > /etc/mysql/conf.d/lowendbox.cnf [mysqld] 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 skip-bdb skip-innodb^DAs
mysqld_safe
script uses /bin/sh
for scripting, it’s also a good idea to check whether dash is used instead of bash.endor:~# apt-get install dash Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: ... Unpacking dash (from .../dash_0.5.4-12_i386.deb) ... Processing triggers for man-db ... Setting up dash (0.5.4-12) ...
endor:~# dpkg-reconfigure dash Adding `diversion of /bin/sh to /bin/sh.distrib by dash' Adding `diversion of /usr/share/man/man1/sh.1.gz to /usr/share/man/man1/sh.distrib.1.gz by dash' endor:~# /etc/init.d/mysql restart Stopping MySQL database server: mysqld. Starting MySQL database server: mysqld. Checking for corrupt, not cleanly closed and upgrade needing tables..One thing I don’t like about Debian 5 is its default inclusion of rsyslog. Well — it’s feature rich, but I don’t need MySQL and TCP syslog support. Weight at 1.2MB RSS is just a bit too fat I reckon. I am not game enough to go without a syslog daemon, so I just go for syslog-ng. Probably not the most light weight, but it’s just something I have been using for the last couple of years.
endor:~# ps -C rsyslogd v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 6441 ? Sl 0:00 0 207 32936 1260 1.9 /usr/sbin/rsyslogd -c3
endor:~# apt-get install syslog-ng && dpkg --purge rsyslog Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: ... Setting up syslog-ng (2.0.9-4.1) ... Starting system logging: syslog-ng. (Reading database ... 16517 files and directories currently installed.) Removing rsyslog ... Purging configuration files for rsyslog ...
endor:~# ps -C syslog-ng v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 8215 ? Ss 0:00 0 105 2754 708 1.0 /usr/sbin/syslog-ng -pShedding 500kb RSS — not too bad I guess :)
Next — Portmap and FAM got installed when Lighttpd was first installed. Lighttpd does not really need FAM. It’s used for stat cache to reduce seeks, but can live without. Not that I have noticed any performance difference anyway for small traffic anyway. Having both of them removed from the process list would give us extra 750KB.
endor:~# apt-get remove --purge portmap eading package lists... Done Building dependency tree Reading state information... Done The following packages will be REMOVED: ...OpenSSH can be replaced by dropbear to save memory.
endor:~# touch /etc/ssh/sshd_not_to_be_run endor:~# apt-get install dropbear ... endor:~# vi /etc/default/dropbear endor:~# /etc/init.d/dropbear start Starting Dropbear SSH server: dropbear.Just remember to set
NO_START=0
in /etc/default/dropbear
so dropbear can run as a daemon. Dropbear daemon is using around 500KB less than OpenSSH daemon during idle, and for each connection it uses 1.5MB less on this Debian 5 box — that’s quite a saving!That’s probably it! Vixie cron can be replaced by a light weight DCRON but I can’t seem to be able find it in Debian’s repository. Exim4 is probably one of the most light weight mail daemon you can have, but then again you might want to question — “do I need a mail daemon running”? You can probably bring it down, and just run
/usr/sbin/runq
once every 2 hours to process the queue, in case the previous delivery failed. That would probably give you another 1MB to play with.You can also use PDKSH to replace BASH as interactive shell to loose some weight.
endor:~# ps -C bash v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 8409 pts/1 Ss 0:00 2 663 2140 1568 2.3 -bash endor:~# apt-get install pdksh endor:~# chsh -s /bin/pdkshThat’s 1 full megabyte off the scale! Also note that VPSLink’s# ps -C pdksh v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 8550 pts/0 Rs 0:00 0 174 1633 588 0.8 -pdksh
/etc/inittab
automatically spawn a BASH process on the console — just in case you got locked out from firewall. For me it’s the last line of inittab
file. Change it to /bin/sh
or /bin/pdksh
, run init q
to reload init(1), and then kill that bash process.Here’s the end result:
# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ... root 325 0.0 0.4 2032 292 ? S<s Mar17 0:00 udevd --daem root 1216 0.0 0.0 0 0 ? S Mar17 0:00 [pdflush] root 6470 0.0 1.3 2048 896 ? Ss Mar17 0:00 /usr/sbin/cron 103 6973 0.0 1.3 6112 912 ? Ss Mar17 0:00 /usr/sbin/exim4 root 7953 0.0 0.7 1716 524 ? S 00:23 0:00 /bin/sh /usr/bi mysql 7992 0.0 8.2 37904 5404 ? Sl 00:23 0:00 /usr/sbin/mysql root 7994 0.0 0.8 1636 536 ? S 00:23 0:00 logger -p daemo root 8215 0.0 1.1 2860 776 ? Ss 00:31 0:00 /usr/sbin/syslo www-data 8313 0.0 2.4 5712 1640 ? S 00:37 0:00 /usr/sbin/light root 8418 0.0 0.7 2052 468 ? Ss 00:51 0:00 /usr/sbin/dropb root 8527 0.0 0.7 1712 468 ? Ss 01:19 0:00 /bin/sh -- root 8549 0.0 1.9 2712 1300 ? Ss 01:21 0:00 /usr/sbin/dropb root 8550 0.0 0.9 1808 600 pts/0 Rs 01:21 0:00 -pdksh root 8562 0.0 1.3 2308 908 pts/0 R+ 01:26 0:00 ps aux # free total used free shared buffers cached Mem: 65704 58852 6852 0 2180 40344 -/+ buffers/cache: 16328 49376 Swap: 131064 380 130684That’s 12MB trimmed, which can be used in disk cache to improve static file serving.
Conclusion
So how do we conclude? 64MB is more than enough to serve a few low traffic static websites. You can actually run a few WordPress sites with a few hundred visitors a day — at the price equivalent to many heavily oversold shared hosting and you get root access!One thing about root access though — in all my examples above I used root account and never bothered to use a normal user account. It is bad from security aspect so don’t do it. Or at least don’t tell anyone that you use nothing but root :)
REFERNCES
http://www.lowendbox.com/blog/yes-you-can-run-18-static-sites-on-a-64mb-link-1-vps/