Install Ubuntu Server Edition, Rails, PHP, Passenger, PostgreSQL, and MySQL
Requirements
My primary goals this time around were:
- Ubuntu 9.04 Server Edition. No GUI! Faster and even more stable.
- Apache only
- 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].
- No Mongrel – Nothing against this web server either. Mongrel too has served me well. Again, funneling everything through Apache is less to manage.
- Phusion Passenger
- 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.
Network
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 address 192.168.1.2 netmask 255.255.255.0 network 192.168.0.0 broadcast 192.168.1.255 gateway 192.168.1.1
Restart Network Services:
/etc/init.d/networking restart
DNS
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
Reboot:
reboot
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.
fsck
Database
I want my websites back online ASAP, so we need to get the DB operational and data dumps imported.
PostgreSQL
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:
# TYPE DATABASE USER CIDR-ADDRESS METHOD # 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 192.168.1.0/24 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
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
MySQL
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 = 127.0.0.1
Restart:
/etc/init.d/mysql restart
Verify:
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
PHP
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 </VirtualHost>
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
</Directory>
</VirtualHost>
Create symlink and restart Apache:
cd ../sites-enabled ln -s ../sites-available/www2.russbrooks.com 001-www2.russbrooks.com apache2ctl restart
Permissions
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:
ssl=yes use=web, web=checkip.dyndns.com/, web-skip='IP Address' ## Dynamic # members.dyndns.org is the default if omitted. # server=members.dyndns.org, protocol=dyndns2, login=YourUN, password=YourPw subdomain.nowhere.org ## Custom custom=yes, protocol=dyndns2, login=YourUN, password=YourPw a.russbrooks.com,bunch.russbrooks.com,of.russbrooks.com,subdomains.russbrooks.com
Restart DDClient:
/etc/init.d/ddclient restart
Ultraviolet
My syntax-highlighting Gem for my blog:
apt-get install libonig-dev gem install ultraviolet
Security
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