25 May 2009

Install Ubuntu Server Edition, Rails, PHP, Passenger, PostgreSQL, and MySQL


My primary goals this time around were:

  1. Ubuntu 9.04 Server Edition. No GUI! Faster and even more stable.
  2. Apache only
  3. No nginx – Nothing against this web server. nginx has served me flawlessly for the last 3 years and it is blazing fast [much faster than Apache]. Frankly, I’m sad to see it go. But, If you are running Rails along side PHP sites, it sometimes makes sense to funnel everything through one web server technology [now that Passenger has made that easy].
  4. No Mongrel – Nothing against this web server either. Mongrel too has served me well. Again, funneling everything through Apache is less to manage.
  5. Phusion Passenger
  6. PHP and Rails sites running in tandem over the same HTTP port. I previously had to use different ports with nginx and Apache running together.

Packages Selected Upon Install

  • LAMP
  • PostgreSQL
  • Mail Server – Selected the “Satellite Server” option, because I’m only using it to send email via my ISP’s mail server.
  • OpenSSH Server

Change Root PW

sudo passwd root

Enter your password first. Then you are asked for new Root PW.


Since this will be a web server among other things, the first order of business is to disable DHCP and instead use a static IP.

Edit /etc/network/interfaces:

su -
nano /etc/network/interfaces

This particular server is connected to my LAN via 1Gb Ethernet, so the network interface ID is “eth0” (Ethernet device 0). Look for the “eth0” section, and make it look as follows, replacing IP address with ones appropriate to your setup.

# The primary network interface
auto eth0
iface eth0 inet static

Restart Network Services:

/etc/init.d/networking restart


Primary and Secondary DNS addresses should pass through from your router. Ping www.google.com to see if an IP comes back. If not, then set up DNS manually by editing resolv.conf:

nano /etc/resolv.conf

Change the IP address in the following line to match your ISP’s DNS.

nameserver xxx.xxx.xxx.xxx

You may have to restart networking again:

/etc/init.d/networking restart

Automount External Disks

I have two external USB drives connected to the machine. Let’s get those automating on boot.

View all drives, mounted or not:

fdisk -l

Shows enough info (capacities, etc.) that you will be able to infer which Device ID each drive has. In my case, my two external drives are sdb1 and sdc1.

View drive UUIDs:

ls /dev/disk/by-uuid -alh

You should be able to tell between the output of those two commands what UUIDs belong with which drives. Simply match up the device IDs. For example, the device ID of my two drives is sdb1 and sdc1.

Create the directories for the mount points:

mkdir /media/disk
mkdir /media/disk-1

Edit File-Systems Table:

nano /etc/fstab

Add these lines, changing UUIDs, mount points, and file systems accordingly for your environment. My external drives are formatted with the Linux “Ext3” file system. Yours may be different.

# 500GB Seagate External
UUID=53c6099b-9c35-4da2-975f-7436c42ff381 /media/disk   ext3  relatime,errors=remount-ro 0  1
# 200GB Seagate External
UUID=ccf0a125-b693-4610-9f4f-417bc8a6f653 /media/disk-1 ext3  relatime,errors=remount-ro 0  1



Here’s a tip if you encounter mount or fsck errors on boot. My fstab lines were initially incorrect, so when I rebooted, I experienced a file-system-check error. I rebooted a few times, changing my fstab lines each time, but could not get it to go away. Finally, a simple fsck after booting did the trick.



I want my websites back online ASAP, so we need to get the DB operational and data dumps imported.


Open TCP/IP Connections

Like a good database should, PostgreSQL comes in a very locked-down state security-wise. It is up to you to increase its surface area just enough to operate it effectively, from only the machines that need to access it, and no more. I connect to PostgreSQL from other machines on the network, so let’s make PostgreSQL listen on more than just localhost.

nano /etc/postgresql/8.3/main/postgresql.conf

Ctrl-W to do a find for “listen_”, and change this line to:

listen_addresses = '*'

Change Password of Postgres User

sudo -u postgres psql template1

Also following proper security practices, Postgres runs as the less-privileged “postgres” user. The above is a good example of the Switch-User Do [sudo] command being used to perform an operation as a user other than Root. The -u postgres parameter is telling Linux to, for the life of this next command, use the Postgres user rather than Root. So, the entire command above is saying, “Execute the psql template1 command as the user ‘postgres’.” psql template1 launches the Postgres command-line app [psql] and logs into the ‘template1’ database. When switching from Root to a user of lesser privilege, you will not be authenticated.

We must do a sudo at this stage rather than simply passing username using the “psql -U postgres” method because by default Ident Authentication is the active authentication method for local connections. Ident Authentication auto-passes your Linux credentials to Postgres. Think of it as “Windows Authentication” in a SQL Server environment. This isn’t necessarily desirable in a web scenario because most web apps are configured to use local SQL users [with an MD5-encrypted password handshake]. We are going to change authentication method to MD5 in a sec.

Enter this SQL code to change the password of the postgres user:

ALTER USER postgres WITH ENCRYPTED password 'your_password';

Enter \q to exit.

Host-Based Authentication

Edit Postgres’ Host-Based Authentication file:

nano /etc/postgresql/8.3/main/pg_hba.conf

Hit Esc-/ to go to the bottom of the file.

Comment out the first line that start with “local”. Then, replace “host all” line under IPv4 as follows, changing IP address accordingly for your network:

# Database administrative login by UNIX sockets
# local   all         postgres                          ident sameuser
# "local" is for Unix domain socket connections only
local   all         all                               md5 
# IPv4 local connections:
host    all         all        md5
# IPv6 local connections:
host    all         all         ::1/128               md5

I’ve also moved the column header line [TYPE DATABASE USER] above all of the other lines.

What we have done above is similar to what is called “SQL Authentication” in the Microsoft world, IOW, all users stored locally in the SQL database. We’re not using IPv6, so forget that line for now. The line beginning with “local” is saying, “All SQL users have access to every database and must authenticate by supplying a password [which is encrypted with the MD5 hash algorithm].”

The first line beginning with “host” is saying, “All remote users must have an IP address of 192.168.1.x and must authenticate by supplying a password [encrypted with MD5] for a local SQL user.”

Restart DB Services

/etc/init.d/postgresql-8.3 restart


Test external connections using pgAdmin III or another PostgreSQL management tool.

Install Instrumentation

pgAdmin III will nag you about Instrumentation not being installed. It’s already on your machine, so installing it is simply:

psql -U postgres -d postgres < /usr/share/postgresql/8.3/contrib/adminpack.sql

Restore Backups

psql -f backup.sql -U postgres your_db


I don’t use MySQL, but inevitably you run into scenarios in which a client is an unfortunate MySQL victim. I have no choice but to keep it around.

nano /etc/mysql/my.cnf

Comment out:

# bind-address           =


/etc/init.d/mysql restart


netstat -tap | grep mysql

Output should be similar to:

tcp    0    0 *:mysql     *:*    LISTEN  19743/mysqld

You were asked for a MySQL root password upon Ubuntu install. You should now be able to connect to the DB using that user account.

Restore Backups

Import any backup dumps:

mysql -p database_name < db_backup-2009-05-23.sql

Web Application Services

Update packages:

apt-get update
apt-get upgrade

Ruby on Rails / Phusion Passenger

There is a Passenger Aptitude package called “libapache2-mod-passenger”, but every time I installed it, it removed the PHP, PostgreSQL, and MySQL Apache modules. The Passenger Gem, on the other hand, was successful, and I am running PHP and Rails in tandem on Apache. I believe the process went like this:

apt-get install build-essential
apt-get install libpq-dev
apt-get install ruby irb rdoc ruby1.8-dev libopenssl-ruby imagemagick
apt-get install rubygems
gem install rails
gem install passenger
gem install postgres
gem install tzinfo

For my blog to work, I had to install slightly older versions of a couple of gems…

gem install -v=2.2.2 rails
gem install -v=1.9.0 liquid

...then ensure the RAILS_GEM_VERSION was set to 2.2.2 in config/environments.rb:

RAILS_GEM_VERSION = '2.2.2' unless defined? RAILS_GEM_VERSION

Follow the install process’s instructions and create a passenger.load file.

nano /etc/apache2/mods-enabled/passenger.load

My file looks as follows. Yours may be different. Change it accordingly, ensuring that the paths are valid.

LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.2/ext/apache2/mod_passenger.so
PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.2
PassengerRuby /usr/bin/ruby1.8
RailsEnv production


apt-get install php5 libapache2-mod-php5 apache2-prefork-dev libapr1-dev php5-gd php5-pgsql php5-mysql

Virtual Hosts

cd /etc/apache2
nano ports.conf

Verizon blocks Port 80, so I have to serve my websites over alternate ports. So ports.conf contains these lines, replacing the Port 80 lines that are there.

NameVirtualHost *:8000
Listen 8000

Edit Virtual Hosts config files for sites.

cd /etc/apache2/sites-available
nano www2.russbrooks.com

This is the contents of the above file for my Rails sites:

<VirtualHost *:8000>
  ServerName www2.russbrooks.com
  DocumentRoot /var/www/rb.com/www/public
  CustomLog /var/www/rb.com/log/access_log common
  ErrorLog /var/www/rb.com/log/error_log

This is a config file for a PHP site:

<VirtualHost *:8000>
  ServerName example.russbrooks.com
  DocumentRoot /var/www/example/www
  CustomLog /var/www/example/log/access_log common
  ErrorLog /var/www/example/log/error_log
  <Directory "/var/www/example/www">
    Order allow,deny
    Allow from all

Create symlink and restart Apache:

cd ../sites-enabled
ln -s ../sites-available/www2.russbrooks.com 001-www2.russbrooks.com
apache2ctl restart


Change ownership of your web apps to the www-data user and group, and change their permissions accordingly:

chown -R www-data:www-data /var/www
chmod -R 755 /var/www

Dynamic DNS Client

I use a Dynamic DNS service, DynDNS, that updates my DNS records every time my ISP [Verizon] changes my IP. There is a package for the client app:

apt-get install ddclient
nano /etc/ddclient.conf

My conf file looks like this:

use=web, web=checkip.dyndns.com/, web-skip='IP Address'

## Dynamic
# members.dyndns.org is the default if omitted.
# server=members.dyndns.org,

## Custom

Restart DDClient:

/etc/init.d/ddclient restart


My syntax-highlighting Gem for my blog:

apt-get install libonig-dev
gem install ultraviolet


Please see my article on Linux Security: Public Key Authentication.

No Samba?

That’s right. No Samba - for too many reasons to list. I use SSH for everything: remote control, networking, and file transfer. Transmit is a great tool in that regard. It allows you to mount remote FTP and SFTP sites as if they are local folders (and much faster than ExpanDrive).

Filed under Linux