CentOS 6.4 server setup with Ruby On Rails, Nginx and PostgreSQL
Server setup with the entire environment for Rails applications can be quite tricky, especially when you do it for the first time. Here is step by step guide how to setup CentOS 6.4 server with a basic environment for deploying Rails applications. I encourage you to choose CentOS Linux - it is a reliable distro (well, based on Red Hat Enterprise Linux), easy to handle and doesn't require advanced Unix knowledge like Gentoo (especially while updating system related stuff).
Initial setup
You need to ssh on your server:
ssh root@your-ip
Start with creating new user deploy:
adduser deploy
And create password for the new user:
passwd deploy
You shouldn't use root user often, but you will need root privileges for performing many tasks, like installing stuff, so it is quite useful to edit sudo configuration - it will give deploy user an ability to perform all tasks which require root privileges by preceding command with sudo
. Run:
visudo
find section that looks like that:
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
and add the following line:
deploy ALL=(ALL) ALL
If you are not familiar with Vi editor, you have to press a
, and then you can type :). When you finish hit escape and type :wq!
.
Enhance security - configure SSH
You can easily make your server more secure by editing SSH configuration. Type:
vi /etc/ssh/sshd_config
Here are some default options which you may change:
#Port 22
#PermitRootLogin yes
Default 22 port for SSH is not insecure, but changing it to some other value will make it more difficult to compromise your server by automated attacks. Pick any number less than 65536 and uncomment this line.
Another option is PermitRootLogin
- change it to no to disable logging as root through ssh. You have root privileges by using sudo
, so you don't need to login as root anyway.
If you are going to create some more users, but you don't want them to login through ssh, add following line:
AllowUsers deploy
When you are finished type:
/etc/init.d/sshd reload
Now, open NEW terminal window and check if everything works:
ssh -p new-port deploy@your-ip
Uhh, what was that IP?
You can avoid typing your IP number on every login by using named hosts, which is quite simple: create or edit ~/.ssh/config
(on your local machine, not server) and add:
Host some-awesome-server-name
Hostname your-ip-number
User deploy
Port your-port
Now you can login on your server by:
ssh some-awesome-server-name
Amazing!
But you can also skip password - you have to generate authentication keys on your local machine:
ssh-keygen -t rsa
And that's the entire output:
Generating public/private rsa key pair.
Enter file in which to save the key (/home/azdaroth/.ssh/id_rsa):
Created directory '/home/azdaroth/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/azdaroth/.ssh/id_rsa.
Your public key has been saved in /home/azdaroth/.ssh/id_rsa.pub.
The key fingerprint is:
d9:1c:0b:76:60:56:e0:af:cd:f3:93:c7:15:f5:dc:dc azdaroth@abyss
The key's randomart image is:
+--[ RSA 2048]----+
| =o. |
| + . .|
| + o o=|
| . B o .E|
| S = .|
| + .|
| . + o . |
| oo o |
| .o |
+-----------------+
What about the passphrase? It's up to you. If you leave it blank, you can ssh on your server by just entering: ssh server-name and that's all. Pretty nice, but if your local machine gets stolen, something really bad may happen with your server. So, you should enter a passphrase, at least on your laptop. The only downside of passphrase is that you will be asked to enter it on each login.
To finish setup on your server, enter the following commands:
mkdir ~/.ssh
touch ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Setting these permissions is essential and ssh will stop working if StrictModes
is set in configuration (and probably is by default). Now, copy your PUBLIC key to authorized_keys file on your server:
cat ~/.ssh/id_rsa.pub | ssh server-name "cat >> ~/.ssh/authorized_keys"
Installing prerequisites
You will need to install some libraries. Start with updates:
sudo yum update
You will probably want to install some extra repositories (like Fedora Epel) to get some up-to-date packages. Run:
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm
And enable remi repository:
sudo vi /etc/yum.repos.d/remi.repo
In [remi]
section set the enabled option to 1.
[remi]
name=Les RPM de remi pour Enterprise Linux 6 - $basearch
#baseurl=http://rpms.famillecollet.com/enterprise/6/remi/$basearch/
mirrorlist=http://rpms.famillecollet.com/enterprise/6/remi/mirror
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
Now, you are going to install some packages, like RVM dependencies and other stuff.
sudo yum install git-core openssl openssl-devel subversion curl curl-devel gcc-c++ patch readline readline-devel zlib zlib-devel libyaml-devel libffi-devel make bzip2 autoconf automake libtool bison sqlite-devel libxml2 libxml2-devel libxslt libxslt-devel libtool
RVM, Ruby and Rails
Now you can proceed to installing Ruby. We will use RVM - command-line tool, which makes managing multiple Ruby versions really easy. To install RVM enter:
\curl -L https://get.rvm.io | bash -s stable
Carefully read generated output. If everything is ok, run
source /home/deploy/.rvm/scripts/rvm
source ~/.bashrc
Make sure you have following lines in your ~/.bashrc
file :
[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
PATH=$PATH:$HOME/.rvm/bin # Add RVM to PATH for scripting
To check if everything was installed properly enter:
type rvm | head -1
It should return something like: rvm is a function
. If not, reload terminal session (simply log out and log in again).
And now you can install specified Ruby Version:
rvm install 2.1.0
Use installed Ruby version as the default one:
rvm use 2.1.0
rvm use 2.1.0 --default
Nice! You've successfully installed Ruby. Now, you can install Bundler and Rails.
gem install bundler rails
Nginx and Passenger
You will need an http server to run your applications. Nginx is fast, lightweight and easy to configure and Phusion Passenger module makes Nginx and Rails integration painless. Firstly, install Passenger gem:
gem install passenger
and then, install Nginx with compiled Passenger module:
rvmsudo passenger-install-nginx-module
Choose the recommended install mode.
Now, open the Nginx configuration file (/opt/nginx/cong/nginx.conf
if you haven't changed it during installation).
sudo vi /opt/nginx/conf/nginx.conf
Let's change some default config. Change worker_processes to be equal to number of CPU cores. You can also enable gzip compression. Just add following lines:
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
Now setup your Rails application:
server {
listen 80;
server_name some-example.com;
root /path-to-your-application/current/public;
client_max_body_size 128M;
passenger_enabled on;
rails_env production;
location ~ ^/(assets|images|javascripts|stylesheets|system)/ {
expires max;
add_header Cache-Control public;
}
}
At the end it should look like that:
#user nobody;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
passenger_root /home/azdaroth/.rvm/gems/ruby-2.1.0/gems/passenger-4.0.35;
passenger_ruby /home/azdaroth/.rvm/wrappers/ruby-2.1.0/ruby;
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 80;
server_name some-example.com;
root /path-to-your-application/current/public;
client_max_body_size 128M;
passenger_enabled on;
rails_env production;
location ~ ^/(assets|images|javascripts|stylesheets|system)/ {
expires max;
add_header Cache-Control public;
}
}
}
You should also use a neat little script for easier Nginx management. Open the following file:
sudo vi /etc/init.d/nginx
copy & paste script below:
#!/bin/sh
#
# nginx - this script starts and stops the nginx daemin
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /opt/nginx/conf/nginx.conf
# pidfile: /opt/nginx/logs/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
nginx="/opt/nginx/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/opt/nginx/conf/nginx.conf"
lockfile=/var/lock/subsys/nginx
start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}
restart() {
configtest || return $?
stop
start
}
reload() {
configtest || return $?
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
echo
}
force_reload() {
restart
}
configtest() {
$nginx -t -c $NGINX_CONF_FILE
}
rh_status() {
status $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esac
and make it executable:
sudo chmod +x /etc/init.d/nginx
Now you can control Nginx by few commands:
sudo service nginx start
sudo service nginx stop
sudo service nginx reload
sudo service nginx restart
sudo service nginx status
sudo service nginx configtest
To add Nginx to the default run levels, enter:
sudo /sbin/chkconfig nginx on
ImageMagick
This part is optional, but you will probably need ImageMagick in some applications - it is a powerful software for creating, editing and converting images. The ImageMagick version available in repositories is probably outdated, so we will compile it from source.
Start with installing some delegates (you have to install them before compiling ImageMagick).
sudo yum install libjpeg libjpeg-devel libpng-devel libpng-devel freetype freetype-devel libtiff-devel jasper-devel bzip2-devel giflib-devel ghostscript-devel
And compile it:
wget http://www.imagemagick.org/download/ImageMagick.tar.gz
tar xvfz ImageMagick.tar.gz
cd ImageMagick-6.8.8-2
./configure
make
sudo make install
sudo ldconfig /usr/local/lib
make check
Export PATH
variable:
export PATH=$PATH:/usr/local/bin
And check if everything works:
convert -version
You should get output similar to this:
Version: ImageMagick 6.8.8-2 2013-07-18 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC OpenMP
Delegates: bzlib freetype jng jp2 jpeg png ps tiff xml zlib
PostgreSQL
So, the last step is installing PostgreSQL. Firstly, you have to modify /etc/yum.repos.d/CentOS-Base.repo
file:
sudo vi /etc/yum.repos.d/CentOS-Base.repo
and in both [base]
and [updates]
sections add the following line:
exclude=postgresql*
Now, install PostgreSQL repository and PostgreSQL itself:
sudo rpm -Uvh http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm
sudo yum install postgresql93 postgresql93-devel postgresql93-server postgresql93-libs postgresql93-contrib
You have to initialize database cluster before doing anything:
sudo /etc/init.d/postgresql-9.3 initdb
And you can start Postgres and add it do default run levels:
sudo service postgresql-9.3 start
sudo chkconfig --levels 235 postgresql-9.3 on
The very first thing you should do with Postgres is setting password for postgres user:
sudo su postgres
psql
Now, you are in psql console. To change password, enter:
alter user postgres with password 'postgres-user-password';
Logout from postgres user and modify /var/lib/pgsql/9.3/data/pg_hba.conf
:
sudo vi /var/lib/pgsql/9.3/data/pg_hba.conf
At the bottom of the file change authentication method to md5:
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all md5
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 md5
and restart Postgres:
sudo service postgresql-9.3 restart
To run psql console as deploy user just enter:
psql postgres postgres
and create a new user and database:
create user username with password 'secretPassword';
create database testdb owner=username;
And that's it! Enjoy your server.