Friday, December 07, 2012

Ubuntu 12.04 Building your Own Local Ruby Gem Server using RBENV / NGINX Unicorn

There are times you would like to run your own gem server. You may want to share gems with colleagues when you are both without internet connectivity. You may have private code, internal to your organization, that you’d like to distribute and manage as gems without making the source publicly available. In this blog post, I've configured Ubuntu 12.04, RBENV, Nginx and Unicorn to serve Ruby Gems locally.

Install RBENV

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

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/geminabox

chmod +x /etc/init.d/geminabox

/etc/init.d/geminabox 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 /var/www/geminabox/app/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/geminabox/"
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/geminabox.socket"

# 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/geminabox

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/geminabox.socket fail_timeout=0;

server {
listen                80; # default;
server_name ;
root                  /var/www/geminabox/;
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/geminabox /etc/nginx/sites-enabled/geminabox

Start on Boot
update-rc.d geminabox defaults

Gem in a Box

cd /var
mkdir www
cd www
mkdir geminabox
cd geminabox
mkdir data


require "rubygems"
require "geminabox" = "/var/www/geminabox/data" # ... or wherever
run Geminabox

vim Gemfile

gem "unicorn"
gem "geminabox"

No comments: