I am running my personal server on a rather small Rackspace Cloud machine with 256M of memory. A default LAMP (plus a couple Python web apps) install seems to occasionally (my websites are usually not THAT busy) choke and force a reboot. Let's try and avoid that:
First thing I did was install a PHP op code cache called php5-xcache that I have used before. It is a zero configuration slam-dunk, and my spidy sense suggests that it produces a tangible speed improvement.
All of the reading I am doing (see references below) prominently mention unloading unused Apache modules so as to reduce the memory footprint of Apache processes, so this seems like the first place to start. This is a minimal (not requiring hacking my default Ubuntu server Apache configuration too much) list of Apache modules I can get by on at the moment:
alias.conf cgi.load dir.conf mime.conf php5.load ssl.load wsgi.conf alias.load deflate.conf dir.load mime.load rewrite.load status.conf wsgi.load authz_host.load deflate.load headers.load php5.conf ssl.conf status.load
Now into the murky world of Apache MPM (Multi-Processing Modules). Reading the "Compile-Time Configuration Issues" section in , one is left with the definite impression that the worker MPM would be a better choice then the prefork MPM in a limited memory situation. So why does prefork always seem to be installed? A little experiment tells all:
apt-get install apache2-mpm-worker
will result in the removal not only of apache2-mpm-prefork, but also all PHP modules!  has the answer:
"Apache PHP module is reputed to be unstable in multi-threaded environments"
 goes on to describe a somewhat complicated procedure for making the multi-threaded apache2-mpm-worker module work with PHP. Maybe later, if all else fails. Or maybe switch to lighttpd as a web server. Now back to the stodgy old Apache prefork MPM....
 gives some nice details on how one might go about optimizing the prefork MPM configuration. top is telling me that my Apache processes are currently consuming roughly 30+M per process. My current default configuration is this:
which I am going to crank WAY down to this:
StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxClients 150 MaxRequestsPerChild 0
StartServers 1 MinSpareServers 1 MaxSpareServers 2 MaxClients 5 MaxRequestsPerChild 200
Interesting that the default value of MaxRequestsPerChild was zero. Apparently the point of setting this value to non-zero is to periodically force Apache processes to be killed and recreated, to combat memory bloat (aparently they do not release memory?). It is also worth noting that my server's CPU usage tends to hangout near zero, while memory is always maxed out and swapping is a regular cause of problems. In top I have seen a single Apache process top out at as high as 50% of memory. I think killing processes frequently to reduce memory overhead is probably the way to go.
And finally,  recommends setting a really small KeepAliveTimeout of 2. And  recommends keeping "Timeout" small, since I have so few processes and do not want them all to be waiting for a timeout at the same time. So I went for a Timeout of only 10 seconds.
***Update: with the current state of my Apache and MySQL tuning efforts, the graphs of my server's memory usage have gone from a state of consistently zero free memory and wild swings in swap usage and swapping, to a fairly consistent state of some free memory, with the occasional spike that leads to zero free memory and a LITTLE BIT of swap usage and swapping. (This with 256M of memory.) Things seem much more stable, but only time will tell if I have completely solved the problem of the machine sometimes swapping itself to death.
This is a continuation of my related post on Apache performance tuning.
I found a very informative little tool call "mysqltuner" in the Debian archives. Upon running, it gives a nice concise breakdown of what is going on with my MySQL server performance-wise, followed by some suggestions for how to increase performance.
I found out, for instance, that the default values for interactive_timeout and wait_timeout were set to (what seems like an outrageous) value of 8 hours. I cranked them back to two minutes and mysqltuner stopped complaining.
mysqltuner also tells me "Total buffers: 58.0M global + 2.7M per thread" which implies that cutting back on max_connections (number of threads) will have quite a limited impact. But my memory is really tight, so let's do that anyway. I went with 10 for max_connections: 5 Apache servers plus padding for phpmyadmin connections, among other things. (mysqltuner will warn me in the future if I am bumping up against this limit.)
mysqltuner also suggests increasing some memory constraints on certain MySQL functions, but I am going to hold off on that until I see what happens with the current setup for a couple of days.
 also talks about turning off the InnoDB engine with "skip-innodb" in order to save a pile of memory, but one of my databases is using InnoDB tables, and when I try to convert them to myisam it refuses while complaining about "foreign keys". I believe that database is using an InnoDB feature where, if I delete a row in one table dependent rows in other tables are also automatically deleted, so I guess I will live with it for the moment.