Talking tech since 2003

In the past, I have discussed the need to have strong and reliable web hosting, and even went on to suggest things to look at when shopping for a provider.  For larger websites – or for people own a number off websites – shared hosting (while incredibly affordable and simple to configure) isn’t always a great option simply because of the lack of power and dedicated resources to the end-user.  Because of this, I always recommend that users with large (and growing) web needs consider the use of virtual private server (VPS) or dedicated server environments.

Asides from price, one of the biggest things that prevents users from opting to go the VPS/dedicated server route is the fact that the end-user has to configure everything from the ground up.  Trust me, we’ve all been there.  Staring at a blank Linux console can be very intimidating.  But how exactly do you turn that console into a functional and reliable web server?

This tutorial will cover the process of setting up a web server – complete with the Lighttpd web server, MySQL database server, PHP scripting language, and Webmin administrative console – on a Debian Linux server.  While you can (likely) use this same configuration on other server platforms, I am opting to use Debian because I feel it is a more stable environment.

Once you’ve logged in to SSH with the root credentials configured or provided to you by your host or provider, you will want to update the software repository list and perform an upgrade of the system’s software.  To do this, issue the two commands below.

apt-get update
apt-get upgrade

With that done, you can install the server-side applications required for your web server using Debian’s package system, “apt-get”.

apt-get install mysql-server lighttpd php5-cgi php5-mysql

After a couple of minutes (depending on your connection speed), you should have all of the packages downloaded and installed.  From there, you will want to go about configuring and using them.

One of the first things that I do when setting up a web server is update the MySQL configuration.  You see, out of the box, MySQL can be quite a memory hog.  To change the configuration of MySQL, we will need to edit the “my.cnf” file with a text editor.  In this case, I am using nano.  However, because there is always going to be room for error, we will back up the existing configuration file before making any changes to it.  This way, if we break things with the low-memory configuration file, we can reference or restore the old one.

cp /etc/mysql/my.cnf /etc/mysql/my_backup.cnf

Now that that is done, we can proceed with actually editing and configuring the “my.cnf” file.  To do this, call “nano” – the text editor – with the location of the file.

nano /etc/mysql/my.cnf

Once “nano” is open, go ahead and delete all of the contents of the MySQL configuration file by pressing and holding down the “CTRL + K” key combination.  With the file empty on your screen, copy and paste in the following configuration.

port = 3306
socket = /var/run/mysqld/mysqld.sock
key_buffer = 16K
#max_allowed_packet = 16K
table_cache = 1
sort_buffer_size = 16K
read_buffer_size = 16K
read_rnd_buffer_size = 1K
net_buffer_length = 1K
thread_stack = 16K
server-id = 1
query_cache_limit = 1048576
query_cache_size = 1048576
query_cache_type = 1
#max_allowed_packet = 16K
key_buffer = 16K
sort_buffer_size = 16K
key_buffer = 16K
sort_buffer_size = 16K

Be sure to note, however, that this is only a sample configuration file obtained from another author online and that while it works perfect for my needs, it may not suffice for you.  For example, this MySQL configuration will only accept connections from the local machine.  While this works great for data-driven web systems such as WordPress or Drupal, you cannot expect to connect a desktop-based accounting application, for example, to the MySQL server.

Once we have made our changes, we will close “nano” and save the file by using the “CTRL + X” key combinations, selecting “Y” when asked to save the file, and hitting our return key to verify the data location.

Now, go ahead and restart the MySQL server by using the following command.

/etc/init.d/mysql restart

If all goes well, MySQL should restart successfully with the new configuration file.  In the event that you have any problems, you should reference the backed up configuration file to see what you may have done wrong, or if need be restore the file completely.  However, I have personally been using this MySQL configuration for about a year now and have never run into any sort of problem.

Now that MySQL isn’t eating RAM like it’s at a cheap Chinese buffet, you can go on to create the folder where your websites will be stored.  In this example, we will create a “web” folder in the /home directory, and then create two sub-folders for storing logs and actual websites.

mkdir /home/web
mkdir /home/web/logs
mkdir /home/web/sites

With the “/home/web” directory tree created, we can go ahead and change the ownership and permissions of the folders.  As Lighttpd creates a “www-data” user and group during the installation process, we will grant said user and group access.  This is because Lighttpd runs as “www-data” and needs access to the folders in order to function.

chown -R www-data:www-data /home/web
chmod 775 /home/web
chmod 775 /home/web/sites
chmod 770 /home/web/logs

What we just did was transfer ownership to the “www-data” user and group and made the “web” and “sites” directory browsable, while disallowing write accesss.

With all of that said and done, we can pop open our Lighttpd configuration file so that we can make the changes necessary for it to serve content.  Again, we are going to back up the original file first so that we can revert back in the event that our modifications cause the daemon to go haywire.

cp /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd_backup.conf
nano /etc/lighttpd/lighttpd.conf

Once we’ve ventured again into the wonderful world of “nano”, we will want to change the value of “accesslog.filename” so that it writes the HTTPd access log to the newly created “logs” directory.  While you can navigate “nano” by using your keyboard’s arrow keys, you may simply opt to use the search option (CTRL + W) to search for “accesslog.filename”.

#### accesslog module
accesslog.filename = “/home/web/logs/access.log”

Likewise, we will want to go ahead and edit the value for “server.errorlog” so that that file too is stored in the proper directory.

## where to send error-messages to
server.errorlog = “/home/web/logs/error.log”

Now, we want to use the down arrow to navigate all the way to the bottom of the configuration.  Once we’re there, we will want to add the following chunk.  What this will do is direct all “www.” traffic to your root domain.  For example, if a user visits “”, they will immediately be transferred over to “” without the “www”.  This is thought to help search-engine optimization by keeping all of your content on one domain (as opposed to on www and non-www domains), and more importantly helps us set up the next part of the configuration where we will create “vhosts” to serve multiple sites from the same server.

server.modules += ( “mod_rewrite”, “mod_redirect” )
$HTTP[“host”] =~ “^www\.(.*)” {
url.redirect = ( “^/(.*)” => “http://%1/$1” )

Now comes the fun part.  We’re going to use Lighttpd’s “mod_evhost” module to automatically direct traffic for a given to the appropriate directory.  In simple English, this following chunk will look at the domain name that a visitor is using and automatically serve the content from the respective folder in “/home/web/sites”.  For example, if a user visited a domain “” that was pointed at your server, the content would be served from “/home/web/sites/”.  This configuration will also accept traffic from one subdomain level so that “” will automatically be served from “/home/web/sites/”.

server.modules += ( “mod_evhost” )
evhost.path-pattern = “/home/web/sites/%2.%1/”
$HTTP[“host”] =~ “^[^.]+\.[^.]+$” {
evhost.path-pattern = “/home/web/sites/%2.%1/”
$HTTP[“host”] =~ “^[^.]+\.[^.]+\.[^.]+$” {
evhost.path-pattern = “/home/web/sites/%3.%2.%1/”

Last but not least, we will enable and configure the “mod_fastcgi” module so that the Lighttpd server can serve PHP files.  This is a must-have for dynamic content such as WordPress-enabled sites.

server.modules += ( “mod_fastcgi” )
fastcgi.server = ( “.php” => ((
“bin-path” => “/usr/bin/php-cgi”,
“socket” => “/tmp/php.socket”

Once we have successfully configured Lighttpd, we want to save the configuration file and exit the text editor.  Like with the MySQL configuration, we will simply use the CTRL + X key combination, hit “Y” when asked to verify the changes, and press return on our keyboard to save the file to the original location.

Now it’s time to cross our fingers and hope that everything worked.

/etc/init.d/lighttpd restart

With a working Lighttpd configuration, we need to set up the server so that users can actually publish the content that will be served from their domains.  Using this type of configuration, we need to create a user so that a person can log into the server and be granted appropriate access.  Regardless of if a user will be controlling one or one-hundred domains, they need a system account.  To create this account (and set the password), use the commands below.  Be sure to replace “user” with the actual login that you want to create.

useradd user --create-home
passwd user

Setting up domains requires you to to create the folder where the domain will reside, and grant the new user access to that folder.  To start this process, we will create a system group for the domain name.  So if your domain name was “”, you would change “domain.tld” to “” in the following command.

groupadd domain.tld

With the group created, add the “www-data” user as well as the newly created user to the group.  This will allow the user to add/change files and for the HTTPd to be able to serve them.

usermod -a -G domain.tld www-data
usermod -a -G domain.tld user

The next step is to make the actual folder for the domain, change the ownership of it so that the HTTPd user and newly created group have access, and create a link to the domain folder in the user’s home directory.

mkdir /home/web/sites/domain.tld
chown www-data:domain.tld /home/web/sites/domain.tld
chmod 7770 /home/web/sites/domain.tld
ln -s /home/web/sites/domain.tld /home/user/domain.tld

If you have another user that you want to give access to the site, you will need to create a user (using the “useradd” and “passwd” commands shown above).  Once the user is created – if it isn’t already – add said user to the domain’s system group and link the domain folder to the user’s home directory.

usermod -a -G domain.tld user
ln -s /home/web/sites/domain.tld /home/user/domain.tld

Rinse and repeat for as many users and domains as you need.

Last but not least, I typically opt to install Webmin – a web-based server administration panel.  Even though I like to manage my web server via the command-line interface, Webmin is a lot easier to use for MySQL administration.  For Linux newcomers, it also gives you a nice set of options as far as system management goes, and can honestly save you quite a bit of time and effort.

Install the dependencies through the repository.

apt-get install perl libnet-ssleay-perl openssl libauthen-pam-perl libpam-runtime libio-pty-perl apt-show-versions

Download the Webmin package.


Install the .deb file using the dpkg package installer.

dpkg --install webmin_1.530_all.deb

Now you can wipe the sweat off your forehead.  You configured a Linux server!  With this basic configuration, you should be able to run a web server with ease.  However, it’s important to note that this is only a basic example configuration.  You will likely need to tweak things to work for your specific needs.

One of the most important things that you need to consider is that this configuration prevents unauthorized users from accessing/modifying your site via SSH/terminal, but because the HTTPd executes all PHP as the same user, any system user with access to any website could – in theory – gain unauthorized access to a website directory by executing malicious PHP.  In practice, “shared hosting” providers should run a separate HTTPd for each site on a system.  However, for this type of setup it is not really ideal simply because of the effort that would be required to set up multiple HTTPd’s.

Now, you can point all of your domains to the IP of your server.  Additionally, you can navigate to https://youserverip:1000 to access Webmin.

If you have any questions, feel free to start a thread in the BestTechie forums.  Good luck!

never recommend setting up any type of configuration on a production server.  You should always test the configuration (and your knowledge) on a non-production server before even thinking about running it in a production environment.  This saves a great number of headaches and eliminates downtime and potential data-loss.  Use this configuration at your own risk.

Post-Publishing Update: This configuration does not include setting up an FTP server simply because FTP is such a dated protocol.  Because the server has an SSHd installed, users can easily log in via “SFTP”, “SSH”, or “SCP”; a protocol that most FTP clients have become compatible with.  Also, I would like to point out that the linked folders in the user home directories aren’t by any stretch of the imagine necessary because users logging in to the server via SSH can browse the /home/web/sites directory.  I only do that for organizational purposes.  However, if you install an FTPd down the road and choose to “jail” users to their home directory, you will find that the symlinks do not work because they are outside of the jailed root.  The best way that I have found to alleviate this issue is to mount the folders instead of symlinking them.  This can be achieved by making a directory for the site in the user’s home folder (“mkdir /home/user/domain.tld”), adding a line for the mount in the fstab file (“echo ‘/home/web/sites/domain.tld /home/user/domain.tld none rw,bind 0 0’ >> /etc/fstab”), and updating the mounts (“mount -a”).

You've successfully subscribed to BestTechie
Welcome back! You've successfully signed in.
Great! You've successfully signed up.
Your link has expired
Success! Your account is fully activated, you now have access to all content.