How to start rails or puma (installed with rvm) with systemd

Just a few notes I took about how to start rails server or puma (installed with rvm) with systemd.

I started reading this question on StackOverflow, but it has no answers. Then I found out this gist containing an interesting example: it shows how to create a wrapper with rvm, and then how to use this wrapper to start the server.

I read more in systemd.service documentation and the rails command line documentation and wrote this (gist):

# based on
# create systemd service file for rails/puma startup
# 0. [if required: rvm use ruby@default]
# 1. rvm wrapper default systemd rails
# 2. put this file in /etc/systemd/system/rails-puma.service
# 3. systemctl enable rails-puma
# 4. systemctl start rails-puma
Description=Rails-Puma Webserver
ExecStart=/home/app/.rvm/bin/systemd_rails server -e production

I didn’t get to test it yet, if it works for you let me know :)

DataMapper out of range value for column ‘id’

If you are reading this, you are (probably) using DataMapper and are having problems like this:

Exception `DataObjects::DataError' at dm-do-adapter/adapter.rb:279
Out of range value for column 'id' at row 1 (code: 1264, sql state: 22003, 
query: INSERT INTO `workspaces` (`id`, `name`) VALUES (2348944937325, 'Stardata S.r.l.')

The problem is that the default field created by DataMapper for an Integer property is a 32bit INT (at least on MySQL 5.5). If you want to store bigger integers you should use the :min and :max parameters, for example:

class Workspace
  include DataMapper::Resource

  property :id, Integer, :key => true, :min => 0, :max => 281474976710656
  property :name, String


Then DataMapper will use a BIGINT for the field.

Running Sinatra (and other Rack apps) on Nginx + Unicorn

Like it often happens with Open Source projects, documentation for Unicorn is quite a mess. I was searching for a tutorial on how to deploy a simple ‘Hello, world!’ Sinatra app on Nginx + Unicorn, but all tutorials I found were somewhat lacking. With some help from vjt I managed to get it up and running, so here’s the breakdown. Most config files are adapted from original project examples.

As you can imagine, the first step is to install all the packages. I’m using an Ubuntu 10.04 virtual machine, so:

user[~]% sudo aptitude install ruby-full rubygems nginx
user[~]% sudo gem install unicorn sinatra

Then, make sure Ruby gems are in PATH:

user[~]% echo 'PATH=$PATH:/var/lib/gems/1.8/bin' >> /etc/environment
user[~]% source /etc/environment
user[~]% echo $PATH
user[~]% unicorn --version
unicorn v4.1.1

Let’s create a basic Sinatra app:

user[~]% mkdir basic; cd basic/
user[~/basic/]% cat > app.rb <<EOF
require 'rubygems'
require 'sinatra'

get '/' do
  "hello world! it's #{} here!"


Let’s verify the app is working with WEBrick (Sinatra development webserver):

user[~/basic/]% ruby app.rb
[2012-01-01 21:03:58] INFO  WEBrick 1.3.1
[2012-01-01 21:03:58] INFO  ruby 1.8.7 (2010-01-10) [x86_64-linux]
== Sinatra/1.3.1 has taken the stage on 4567 for development with backup from WEBrick
[2012-01-01 21:03:58] INFO  WEBrick::HTTPServer#start: pid=1474 port=4567

Try connecting to the host on port 4567:

user[~]% curl http://localhost:4567
hello world! it's Sun Jan 01 21:08:14 +0100 2012 here!

Everything is working fine. Stop WEBrick (Control + C) and let’s create unicorn config files:

user[~/basic/]% cat > <<EOF
require 'sinatra'

set :env,  :production
disable :run

require './app.rb'

run Sinatra::Application

user[~/basic/]% cat > unicorn.conf <<EOF
worker_processes 8
working_directory "/home/USERNAME/basic"
listen 'unix:/tmp/basic.sock', :backlog => 512
timeout 120
pid "/var/run/unicorn/"

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

before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && != old_pid
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us


Make sure /var/run/unicorn is writable by the user that will run unicorn. Try starting the daemon:

user[~]% cd basic
user[~/basic/]% unicorn -c unicorn.conf
I, [2012-01-10T14:13:40.886917 #6288]  INFO -- : unlinking existing socket=/tmp/basic.sock
I, [2012-01-10T14:13:40.887076 #6288]  INFO -- : listening on addr=/tmp/basic.sock fd=3
I, [2012-01-10T14:13:40.887287 #6288]  INFO -- : Refreshing Gem list
I, [2012-01-10T14:13:40.951007 #6291]  INFO -- : worker=2 spawned pid=6291
I, [2012-01-10T14:13:40.951180 #6291]  INFO -- : worker=2 ready
I, [2012-01-10T14:13:40.951426 #6289]  INFO -- : worker=0 spawned pid=6289
I, [2012-01-10T14:13:40.951563 #6289]  INFO -- : worker=0 ready
I, [2012-01-10T14:13:40.951960 #6288]  INFO -- : master process ready

You can check if the (UNIX) socket is up:

user[~]% sudo netstat -nalx
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     17669    /tmp/basic.sock

Now let’s change the nginx configuration. I modified the example from Unicorn website:

user[~]% sudo cat > /etc/nginx/nginx.conf <<EOF
worker_processes 1;


pid /var/run/;
error_log /var/log/nginx/error.log;

events {
  worker_connections 1024;
  accept_mutex off;
  use epoll;

http {
  include mime.types;
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;
  sendfile on;

  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/html text/xml text/css
             text/javascript application/x-javascript

  upstream app_server {
    server unix:/tmp/basic.sock fail_timeout=0;

  server {
    listen 80 default deferred; # for Linux

    client_max_body_size 4G;
    server_name _;

    keepalive_timeout 5;

    # path for static files
    root /home/USERNAME/basic/public;

    # Prefer to serve static files directly from nginx to avoid unnecessary
    # data copies from the application server.
    try_files $uri/index.html $uri.html $uri @app;

    location @app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;


Restart nginx:

user[~]% sudo service nginx restart
Restarting nginx: [warn]: duplicate MIME type "text/html" in /etc/nginx/nginx.conf:68
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful
[warn]: duplicate MIME type "text/html" in /etc/nginx/nginx.conf:68

Check that nginx is running:

user[~]% sudo netstat -nalt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0    *               LISTEN     

Try loading the website:

user[~]% curl localhost
hello world! it's Tue Jan 10 14:21:04 +0100 2012 here!

Here you go! You should work some magic (or just download one of many startup scripts) for running Unicorn at boot time, then sit down and relax :)

Edit: fixed paths and a call to sudo.

Running an external process from Ruby

Running an external process from a Ruby script is quite easy: use backticks (output = `command`) or the %x method (output = %x[command]).

There are more options like system and exec and even more advanced ones like popen, popen3 and popen4 that allow you to manage process stdin/stdout/stderr.

A good resource with more examples is Nate Murray’s 6 Ways to Run Shell Commands in Ruby.

Creare un repo git condiviso

Sulla mia box attuale ho due dischi in raid 1 su cui si trova la /home. Per sviluppare ho una vm QEMU/KVM con sopra Ubuntu 10.04 Server, il cui disco però non risiede sul suddetto raid.

Dato che voglio avere una certa ridondanza per i dati, volevo fare in modo di avere un repository git centralizzato sulla macchina host (nella home, quindi sul raid) da cui clonare nella vm (ed eventualmente sul portatile) per sviluppare e poi pushare di nuovo sul repo sul raid.

Inizialmente ho semplicemente creato un repository sulla macchina host, l’ho clonato sulla vm, ma al momento di effettuare il push usciva questo simpatico errore:

vm ➤ git push
Counting objects: 36, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (23/23), done.
Writing objects: 100% (25/25), 84.45 KiB, done.
Total 25 (delta 6), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To host:c0ding/ruby/project
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'host:c0ding/ruby/project'

Il problema era che essendo loggato anche sulla macchina host ed avendo un repository “aperto”, git si rifiutava di farmi mischiare le due cose. Inizialmente, per risolvere, mi sono limitato a creare un nuovo branch sulla macchina host:

host$ git checkout -b temp-repo

La soluzione più corretta, però, l’ho trovata su stack overflow (e sul sito linkato, pro git): era necessario creare un “bare repository”, ovvero un repository che contiene solo i dati GIT e nessun altro file.

Quindi ho fatto:

host$ rm -rf ~/c0ding/ruby/project
vm ➤ git clone --bare project project.git  # creazione bare repo
vm ➤ scp -r project.git host:c0ding/ruby/  # copia del repo sull'host
vm ➤ rm -rf project project.git            # rimozione vecchi repo sulla vm
vm ➤ git clone host:c0ding/ruby/project.git # clone nuovo repo sulla vm
vm ➤ cd project
vm ➤ git log

commit xxx
Author: Gilberto "Velenux" Ficara
Date:   Mon Nov 1 15:40:20 2010 +0100

Added Cucumber, JQuery support gems. Created DB.

commit xxx
Author: Gilberto "Velenux" Ficara
Date:   Mon Nov 1 14:42:03 2010 +0100

Initial commit

vm ➤