A2 Hosting Ubuntu Virtual Private Server Guide

May 8, 2008

1. Introduction

This guide documents my experience of purchasing a VPS hosting package at A2 Hosting, setting up a fresh Ubuntu server installation, and configuring several typical applications such as Apache, MySQL, PHP via FastCGI, and Ruby on Rails via Mongrel.

I have been a very happy customer of A2 Hosting for over a year and I recommend them very highly. They provide an array of very affordable, high quality hosting packages and their support team has continued to impress me with their responsiveness and receptiveness to feedback.

I hope to create value for myself and others by writing this guide as I work through the process myself. If you find this guide useful and decide to create an account yourself, please do so by visiting them via this link. I will receive a percentage-based commission for every new account created this way.

2. Virtual Private Servers

A virtual private server (VPS) is a “slice” of a real physical server which runs its own operating system and is completely isolated from the rest of the system. Each VPS is given a share of the memory, storage, and CPU time of the physical server as well as its own IP address. Many hosting providers are beginning to offer VPS packages at rates that are approaching that of shared hosting.

VPS hosting has many advantages over shared hosting:

3. Ordering

The ordering process is very simple. First, review the available VPS hosting packages. They differ primarily in terms of hard disk space, memory, and CPU share. A2 allows you to upgrade or downgrade at any time so you can start experimenting with a small base package upgrade later if necessary.

If you need a domain name, they will register one for you at the same time and assign it to your new virtual server. If you have an existing domain at another registrar that you would like to use, you can request to have that domain transferred to A2 or you can opt to leave the domain at the existing registrar and set up the DNS yourself by pointing your domain to A2’s nameservers.

If you place your order during normal (US/Eastern) business hours, you should get a welcome email within a few hours saying your account is ready and providing URLs, usernames and passwords for your HyperVM control panel, DNS management, and SSH login. Your virtual server will have its own unique IP address. Note that SSH is on a nonstandard port for security reasons.

4. Server Configuration

Securing Your New Server

Your initial password is emailed to you in plain text. This same password is used for the A2 Hosting billing system, the HyperVM control panel, the DNS control panel, and for loging in to the server via SSH. You should change these immediately for security reasons.

Installing Ubuntu

By default the CentOS 5 image is used. This guide was written for the Ubuntu 7.10 barebones server image. To use the Ubuntu image, visit the HyperVM control panel using the link you received in the welcome email. Choose Console | Rebuild and select the ubuntu-7.10-i386-a2hosting image. This image has the added benefit that it’s very small—only 135.5 MB—leaving plenty of space for data and applications.

First steps

This guide involves editing a lot of configuration files. I will sometimes write commands that assume that you will be using vi. If you’re not comfortable with vi, you may want to install something else such as nano.

You can now login (as root) to your new Ubuntu VPS using the root password that was emailed to you:

% ssh -p 7822 <IP ADDRESS> -l root

Immediately change your password:

# passwd

Next, there is a small issue with the locale settings that you can go ahead and fix now. Otherwise, you might receive warning messages like “perl: warning: Setting locale failed” when trying to install various programs. To fix this, run:

apt-get install --reinstall language-pack-en

or replace the en with your desired language. Generating the locales will take a couple of minutes.

Next, update your /etc/apt/sources.list to enable a wider selection of packages from universe, multiverse, restricted, security updates, and backports:

deb http://us.archive.ubuntu.com/ubuntu/ gutsy main universe multiverse restricted
deb http://us.archive.ubuntu.com/ubuntu/ gutsy-updates main restricted
deb http://us.archive.ubuntu.com/ubuntu/ gutsy-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu gutsy-security main restricted universe multiverse

Update the package database:

# apt-get update

Upgrade to the latest version of all base packages:

# apt-get upgrade

After that, you might want to install a few extra packages (apt-get install <package>) to make life a little easier:

Adding a New User

You should do most of your work as a normal user instead of as root so that later on you can’t take down your system and precious data with a simple typo. First, install the adduser package and then add the new user:

# apt-get install adduser
# adduser <username>

Before you log out of the root account, you want to make sure the normal user account can run commands as root using sudo. Edit /etc/sudoers and add the following to the end:

<username>  ALL=(ALL) ALL

Now, log out (type exit or press CTRL+D) and log in again as the normal user:

% ssh -p7822 <IP ADDRESS> -l <username>

Securing Your SSH Server

By default, VPS systems at A2 Hosting use port 7822 instead of 22, the default. This will provide some protection from brute force attacks, but it’s still possible for someone to scan your ports and find the SSH server. There are a few more precautions you can take by making the following changes in /etc/ssh/sshd_config:

Once you are done, restart the SSH server:

% sudo /etc/init.d/ssh restart

5. DNS

Existing Domain

To point an existing domain at another registrar to your VPS you will need to use the DNS control panel provided by the current registrar as well as the DNS management URL and nameserver IP addresses provided in the A2 Hosting welcome email.

First, change the nameservers to the two IP addresses that A2 Hosting provided in the welcome email:

Then, login to A2’s DNS management site and add a new DNS zone for the domain:

It may take a few hours for the new entry to propagate. Your domain should now point to the VPS. You can check this by running nslookup foo.com. The changes could take up to a day to propagate so don’t panic if it doesn’t happen fast.

You can point many different domains to the same VPS and configure Apache to serve them as virtual hosts (below).

6. Apache

Install Apache:

% sudo apt-get install apache2

The configuration files are located in /etc/apache2 and the default document root is in /var/www.

When the installation finishes, the system will try to start Apache but depending on your VPS package, there may not be enough memory available. You can check to see if it started by visiting your VPS’s IP address in your browser or typing ps aux and looking for apache2. If not, you will need to follow the steps below to reduce its memory consumption.

Performance tuning

By default, Apache is configured to spawn many different threads when it starts. Depending on which package you chose, it may not even start due to insufficient memory. If your site does not handle enough traffic to warrant a dedicated server, you can most likely stand to decrease the Apache defaults a bit.

In /etc/apache2/apache2.conf, consider each of the following changes:

Multi-Processing Modules

Apache is designed to use Multi-Processing Modules, or MPMs, which bind to a port on the server, accept incoming requests, and send the requests to child processes to be handled. On Ubuntu, Apache is compiled to use “worker” MPMs (as opposed to “prefork”). You can check this by running apache2 -l:

% apache2 -l
Compiled in modules:
  core.c
  mod_log_config.c
  mod_logio.c
  worker.c
  http_core.c
  mod_so.c

From the Apache documentation on the worker MPM:

This Multi-Processing Module (MPM) implements a hybrid multi-process multi-threaded server. By using threads to serve requests, it is able to serve a large number of requests with less system resources than a process-based server. Yet it retains much of the stability of a process-based server by keeping multiple processes available, each with many threads.

On Linux, each child process will use 8MB of memory by default. This is probably unnecessary. You can decrease the overall memory used by Apache by setting ThreadStackSize to 1MB in /etc/apache2/httpd.conf:

ThreadStackSize 1000000

I’m presently experimenting with 1MB but I may move it up to 2MB (or down to 512K) if needed, depending on the requirements of any modules I need to load. There is an informative thread regarding memory usage on the apache-dev list.

You will almost certainly need to decrease the default number of child processes loaded. The number of processes started automatically is governed by the StartServers directive. Each process has ThreadsPerChild server threads plus one listener thread which passes incoming connections off to server threads. Apache also maintains a pool of of spare threads that stand by and wait to handle any new requests. New processes are started or existing ones are killed so that there are always at least MinSpareThreads and at most MaxSpareThreads of these spare threads.

MaxRequestsPerChild sets the maximum number of requests served by a child process before it is recycled. There may be a number of processes which are ready to terminate but which have threads that are still serving clients. Up to MaxClients of these processes are allowed.

Here are the default settings:

<IfModule mpm_worker_module>
    StartServers          2
    MaxClients          150
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadsPerChild      25
    MaxRequestsPerChild   0
</IfModule>

I reduced them as follows:

<IfModule mpm_worker_module>
    StartServers           2
    MaxClients            50
    MinSpareThreads       10
    MaxSpareThreads       25
    ThreadsPerChild       10
    MaxRequestsPerChild  500
</IfModule>

Virtual Hosts

To enable name-based virtual hosting, place the line

NameVirtualHost 66.103.254.221:80

(replace this with your own IP) somewhere in your apache2.conf before the line

Include /etc/apache2/sites-enabled/

In /etc/apache2/sites-available create a file for each virtual host, say /etc/apache2/sites-available/jblevins.org. In this file, put something like the following:

<VirtualHost 66.103.254.221:80>
    ServerName jblevins.org
    ServerAlias www.jblevins.org

    ServerAdmin jrblevin@sdf.lonestar.org

    DocumentRoot /var/www/jblevins.org
    <Directory /var/www/jblevins.org>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
    </Directory>

    ErrorLog /var/log/apache2/jblevins.org-error.log
    LogLevel warn
    CustomLog /var/log/apache2/jblevins.org-access.log combined
    ServerSignature On
</VirtualHost>

In this example, 66.103.254.221 is the IP address of the VPS (replace this with your own), 80 is the port that Apache is listening on, the document root for jblevins.org is /var/www/jblevins.org and there will be separate access and error logs for this domain in /var/log.

Create a symbolic link in sites-enabled to the configuration file in sites-available:

% cd /etc/apache2/sites-enabled
% sudo ln -s ../sites-available/jblevins.org 001-jblevins.org

Restart apache:

% sudo /etc/init.d/apache2 restart

A Note About Permissions

Note that by default on Ubuntu, Apache runs with user www-data and group www-data. Apache needs to be able to read your files so wherever you place your document root, the files will need to be either world-readable or owned by www-data. Furthermore, if you will be running CGI scripts that need to write to files, they will need to be writable by the user or group www-data. Any new file that is created will be owned by www-data and thus you will not be able to modify it under your usual username.

A simple but inelegant solution is to simply add yourself to the www-data group:

% sudo adduser <username> www-data

If your VPS is effectively a single-user system, this should be good enough.

There is a more elegant way around this, but the details and security implications are beyond the scope of this document. If your virtual host’s DocumentRoot falls under /var/www then you should be able to load mod_suexec and add the line SuexecUserGroup <username> <group> to the VirtualHost block. Apache will then execute any CGI scripts as if they were executed by the given user and group. For more details, take a look at the suexec documentation.

Log Rotation

You can pipe your access and error logs to a program like cronolog which will automatically rotate them for you at specific time intervals (or file sizes). You can install it from the Ubuntu repositories:

% sudo apt-get install cronolog

You can set up monthly log rotation by adding the following to httpd.conf:

CustomLog "|/usr/sbin/cronolog /var/log/apache2/access-%Y%m.log" combined

You can do this at the VirtualHost level and for error logs as well. If you want cronolog to maintain a symbolic link to the current log, use the --symlink option. See the documentation for other options.

Log Splitting

Install vlogger:

% apt-get install vlogger

Change the log format, in /etc/apache2/apache2.conf, to include the virtual host:

[...]
#LogFormat   "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
[...]

Pipe your logs through vlogger:

[...]
CustomLog "| /usr/sbin/vlogger -s access.log /var/log/apache2" combined
[...]

Server Status

Since the server is relatively resource constrained, you might want to keep an eye on it during periods of heavy usage so you can tweak your configuration. You can enable a status page to monitor your server using mod_status. To do this, make sure you have links to status.conf and status.load in /etc/apache2/mods-enabled and add the following to your httpd.conf file:

# Server Status (mod_status)
ExtendedStatus On
<Location /server-status>
SetHandler server-status

Order deny,allow
Deny from all
Allow from .yourdomain.com
</Location>

The ExtendedStatus directive gives you the highest possible level of detail and the Order, Deny, and Allow directives ensure that only visitors from .yourdomain.com can view the site. Alternatively, you can restrict access to specific IP addresses, etc.

7. CPAN

You can then install CPAN Perl modules system-wide by running the following command as root (using the Net::Akismet module as an example):

# cpan install Net::Akismet

8. MySQL

Install the MySQL packages:

% sudo apt-get install mysql-server mysql-client

You will be asked for a root password. Note that by default, MySQL is configured to listen only on localhost. This should be fine for running web applications locally. If you want to allow outside access to the MySQL server, you need to edit /etc/mysql/my.cnf and uncomment the line bind-address = 127.0.0.1.

MySQL will use a ton of memory (on the order of 128 MB) under the default configuration. For now, stop the server:

% sudo /etc/init.d/mysql stop

The MySQL configuration file is /etc/mysql/my.cnf. You probably want to at least disable InnoDB by uncommenting the skip innodb line, saving around 100 MB of memory. Here are few other guides to running MySQL with a small memory footprint:

9. PHP

The following instructions, based on a guide posted to the Ubuntu Forums, show how to use PHP5 in a virtual hosting environment using suexec to maintain separate privileges for each host. FastCGI is used instead of mod_php so that we can continue to use the threaded worker MPM. mod_php is not thread safe and installing it would require using the older prefork MPM which tends to use more memory than the worker MPM. You will need to install the following packages:

% sudo apt-get install libapache2-mod-fastcgi php5-cgi

Then configure and the fastcgi module by adding

FastCgiConfig -pass-header HTTP_AUTHORIZATION

to /etc/apache2/mods-available/fastcgi.conf so that HTTP authorization will work and run

% sudo a2enmod fastcgi

to enable the module.

We need to write a wrapper script for PHP that lies under /var/www since suexec does not permit running scripts outside the DocumentRoot. Create a directory /var/www/php-fastcgi and a wrapper script /var/www/php-fastcgi/php5-fcgi containing the following:

#!/bin/bash
export PHPRC="/etc/php5/cgi"
export PHP_FCGI_MAX_REQUESTS=499
exec /usr/bin/php5-cgi

This script will be used for PHP5. You can do the same for PHP 4 if you like. Now, set the appropriate permissions for the directory and script:

% sudo chown -R www-data.www-data /var/www/php-fastcgi
% sudo chmod +x /var/www/php-fastcgi/php5-fcgi

We need to make this script available as a handler for .php files through the Apache configuration files. You can do this at the server, virtual host, or directory level:

## PHP-FastCGI
<IfModule mod_fastcgi.c>
    <Directory /var/www/php-fastcgi>
        SetHandler fastcgi-script
        Options ExecCGI FollowSymLinks
    </Directory>
    Alias /php-fastcgi /var/www/php-fastcgi
    AddHandler php5-fcgi .php
    Action php5-fcgi /php-fastcgi/php5-fcgi
</IfModule>

Now, assuming you have used SuexecUserGroup <username> <group> in your virtual host configuration, any .php files should be handled by FastCGI and executed as the specified user and group.

10. Email

To be able to send and receive email you will need to install and configure exim4:

% sudo apt-get install exim4
% sudo dpkg-reconfigure exim4

During the configuration, choose “Internet Site” and listen on IP address: <IP ADDRESS>:127.0.0.1 where <IP ADDRESS> is the IP address of your VPS.

11. Rails and Mongrel

Installing Rails and Mongrel is simple:

% sudo apt-get install ruby rubygems irb rails mongrel

You probably want to create symbolic links under /usr/local/bin as many scripts will look for Ruby there.

% sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
% sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
% sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
% sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb

You can now run your Rails applications with Mongrel using the mongrel_rails start command. Mongrel will run on a port other than 80 but it is fairly straightforward to configure Apache to proxy for Mongrel using mod_proxy.

12. Miscellaneous Notes