Friday, October 12, 2012

Redmine Setup with NGINX/ Unicorn / RBENV Ubuntu 12.04 LTS

In the search for a good project management and issue tracking tool, we were looking at Jira. However, one of the big sore points of Jira was that it was written in Java. Since we are doing so much work with Ruby on Rails, I set out to look for a Rails based project management and issue tracking tool. One of the best open source tools we found recently is called Redmine. If you haven't heard of it, it's actually used quite a bit in the start up world.

The integration with Git is absolutely amazing - Redmine is definitely one of my favourite tools in the software development space. This document below shows how to setup Redmine in an optimal configuration using Ubuntu 12.04 LTS with Postgresql, Nginx, Unicorn and rbenv.


apt-get -y update
apt-get -y upgrade

apt-get install build-essential bison openssl curl git-core zlib1g zlib1g-dev screen libruby libcurl4-openssl-dev libssl-dev libmysqlclient-dev libxml2-dev   libxslt1-dev vim nodejs ssh libreadline-gplv2-dev automake -y

apt-get install python-software-properties -y
apt-get install libmagickwand-dev -y

apt-get install postgresql-server-dev-9.1 -y


sudo apt-get install postgresql-server-dev-9.1 postgresql -y

1) To connect to PostgreSQL from a remote machine modify the

vim /etc/postgresql/9.1/main/postgresql.conf

change the line

listen_addresses = 'localhost'


listen_addresses = ''

exit the file

Change the default postgres user name

sudo -u postgres psql template1
ALTER USER postgres with encrypted password 'postgres';

2) modify the following file
vim /etc/postgresql/9.1/main/pg_hba.conf

Add the lines at the top of the file

local   all         all                         md5
host   all    all        md5

Start / Stop / Restart PostgreSQL

sudo /etc/init.d/postgresql restart

Option 1 - rubyenv

git clone git:// /usr/local/rbenv

# Add rbenv to the path:
echo '# rbenv setup' > /etc/profile.d/
echo 'export RBENV_ROOT=/usr/local/rbenv' >> /etc/profile.d/
echo 'export PATH="$RBENV_ROOT/bin:$PATH"' >> /etc/profile.d/
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/

chmod +x /etc/profile.d/
source /etc/profile.d/

# Install ruby-build:
pushd /tmp
git clone git://
cd ruby-build

# Install Ruby 1.9.3-p125:
rbenv install 1.9.3-p194
rbenv global 1.9.3-p194

# Rehash:
rbenv rehash

gem update
gem install rails

gem install pg
gem install execjs

gem install mysql2

Nginx / Unicorn

add-apt-repository ppa:nginx/stable
apt-get update
apt-get -y install nginx git-core build-essential

gem install unicorn --no-rdoc --no-ri
rbenv rehash

Add the following environment config variables to a file at


create app-specific unicorn init file here and make it executable

vim /etc/init.d/redmine

chmod +x /etc/init.d/redmine

/etc/init.d/redmine start

sample init.d script

#! /bin/bash

# Provides:          unicorn
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn web server
# Description:       starts unicorn
DAEMON_OPTS="-c $APP/config/unicorn.rb -E production -D"
DESC="Unicorn app for $USER"

case "$1" in
       CD_TO_APP_DIR="cd $APP"

       echo -n "Starting $DESC: "
       if [ `whoami` = root ]; then
         su - $USER -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS"
         $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS
       echo "$NAME."
       echo -n "Stopping $DESC: "
       kill -QUIT `cat $PID`
       echo "$NAME."
       echo -n "Restarting $DESC: "
       kill -USR2 `cat $PID`
       echo "$NAME."
       echo -n "Reloading $DESC configuration: "
       kill -HUP `cat $PID`
       echo "$NAME."
       echo "Usage: $NAME {start|stop|restart|reload}" >&2
       exit 1

exit 0


gem “unicorn”
sample unicorn.rb

# See for complete documentation
# This file should go in the config directory of your Rails app e.g. config/unicorn.rb

app_dir = "/var/www/redmine-2.0/"
worker_processes 10
working_directory app_dir

# Load app into the master before forking workers for super-fast
# worker spawn times
preload_app true

# nuke workers after 60 seconds (the default)
timeout 60

# listen on a Unix domain socket and/or a TCP port,

listen 8080 # listen to port 8080 on all TCP interfaces
#listen ""  # listen to port 8080 on the loopback interface
listen "/tmp/"

# Don't set user if you are already running as the user (will cause a massive chown loop of death)
# This is for if you execute as root and become user.
#user '', ''

pid "/var/www/pids/"
stderr_path "#{app_dir}/log/unicorn.stderr.log"
stdout_path "#{app_dir}/log/unicorn.stdout.log"

if GC.respond_to?(:copy_on_write_friendly=)
 GC.copy_on_write_friendly = true

before_fork do |server, worker|
 # the following is highly recomended for Rails + "preload_app true"
 # as there's no need for the master process to hold a connection
 defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

 # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
 # immediately start loading up a new version of itself (loaded with a new
 # version of our app). When this new Unicorn is completely loaded
 # it will begin spawning workers. The first worker spawned will check to
 # see if an .oldbin pidfile exists. If so, this means we've just booted up
 # a new Unicorn and need to tell the old one that it can now die. To do so
 # we send it a QUIT.
 # Using this method we get 0 downtime deploys.

 old_pid = "#{server.config[:pid]}.oldbin"

 if File.exists?(old_pid) && != old_pid
     sig = ( + 1) >= server.worker_processes ? :QUIT : :TTOU
   rescue Errno::ENOENT, Errno::ESRCH
     # someone else did our job for us

after_fork do |server, worker|
 # Unicorn master loads the app then forks off workers - because of the way
 # Unix forking works, we need to make sure we aren't using any of the parent's
 # sockets, e.g. db connection

 defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
 # Redis and Memcached would go here but their connections are established
 # on demand, so the master never opens a socket

sample /etc/nginx/sites-available/

upstream example-workers {
   # fail_timeout=0 means we always retry an upstream even if it failed
   # to return a good HTTP response (in case the Unicorn master nukes a single worker for timing out).
   server unix:/tmp/ fail_timeout=0;

server {
 listen                80; # default;
 server_name ;
 root                  /var/www/redmine-2.0/public;
 client_max_body_size 10m;
 location / {
   access_log          off;

   include proxy_params;
   proxy_redirect off;

   if (-f $request_filename) {
     access_log          off;
     expires             max;

   if (-f $request_filename.html) {
     rewrite (.*) $1.html break;

   if (!-f $request_filename) {
     proxy_pass          http://example-workers;

Link the files in NGINX

ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

Start on Boot
update-rc.d redmine defaults

Install Redmine

cd /var
mkdir www
cd www

git clone git://

mv redmine redmine-2.0
cd redmine-2.0

bundle install --without development test rmagick sqlite


Modify the database.yml

vim config/database.yml

 adapter: postgresql
 database: redmine
 host: localhost
 username: redmine
 password: {Production password}
 encoding: utf8

rake generate_secret_token

RAILS_ENV=production rake db:migrate

RAILS_ENV=production rake redmine:load_default_data

Password is Admin / Admin

For Open ID

System Administration, Backups and Restore

In our situation we are using Postgresql as the database and the files which are uploaded to /var/www/files

Mount the directory

vim /etc/fstab

// /mnt/server/workstation-01-redmine smbfs username=backup,password={password} 0 0

restart the system

Rsync for Files - this will automatically archive all files to the backup directory

rsync -a --delete /var/www/redmine-2.0/files/ /mnt/server/workstation-01-redmine/files/

Make this a cron job so that it executes the sync every minute

Here is an example of the Crontab job /etc/crontab

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

*/1 *   * * *   root    /var/scripts/
30 4,8,12,16,20,24      * * *   root    /var/scripts/
# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

Postgresql Backup and Restore

Create a password file so that the pg_dump will work without a password

vim ~/.pgpass


chmod 0600 .pgpass

This will add the timestamp

pg_dump -U postgres -w > $(date +"%Y%m%d%H%M%S")_redmine.sql redmine

Create the database redmine then execute the command below

psql -U postgres -d redmine -f 20120520142447_redmine.sql


paste the following into openid URL

then click Login and then create your username


Julian Caceres said...

OMG. is a amazing post. I will try it right now.

Julian Caceres said...

OMG. is a amazing post. I will try it right now.