One app, one user, one ruby

… and check why 5600+ Rails engineers read also this

One app, one user, one ruby

There are many ways to install and manage ruby installation in your infrastructure. Some people like rvm, others prefer rbenv, some pack their ruby installation into deb packages. We like nothing.

You ain’t need it in production

Yes, you read it well. Using no tool is better than using some tool if we can simply avoid it. Let me state it clearly: RVM is a great tool for development environment however we do not see much use for it in production. Even in development some of us started to separate projects on higher level using Vagrant or LXC containers but that is another story.

A little background: Our customers usually host their applications using our infrastructure or their own. These solution are based on LXC, XEN, or KVM. Every project has its own container/VM .

Simplest thing that can possibly work

We do not have a globally installed ruby except for ruby coming from system package that is used mostly by littlechef to setup the virtual machine according to our conventions and application requirements. For every application that is a part of bigger project separate user is created and separate ruby is installed in its home directory. If we end up having five users using same ruby version but different ruby installation then fine. Storage is cheap. Easy upgrades are more important.

Running Ruby

Bash

You might wonder how to execute your scripts and run applications with that ruby. We just add the ruby bin path to user $PATH via .bashrc and voilà. Whenever you run something inside bash it just works. And gem binaries are installed into the same directory so they also work properly.

Not Bash?

If your software is not directly executed inside bash you have two options:

  • run it in bash anyway.
  • just use the full path to your ruby.

Example 1

Here is an example. Runit by default executes supervised processes as root. We use su to switch to user with no special abilities. The -c switch allows you to execute a command that will be invoked in a shell. You can use -s, --shell SHELL specify which shell is going to be used. Thanks to such behavior .bashrc is used and $PATH variable is set up properly.

Note: We use exec twice here so that runit ends up monitoring our application-name binary instead of monitoring su or bash shell.

exec su - application-name -c "cd /var/lib/application-name/current/ \
&& exec bundle exec ruby -Ilib ./bin/application-name"

Example 2

And the second mentioned solution is to use full ruby binary (or gem) path in a script:

/var/lib/application-name/1.9.3-p286/bin/rackup path/to/the/app

Cookbook

You can see our open-sourced cookbook for ruby installation on github arkency/ruby-build-cookbook. This is first thing that we open sourced as a company (we do have lot of open source experience as individuals) and we hope that in the future you can expect even more from us.

And if you are not that much into cooking (with chef) you can see for yourself how simple the whole ruby installation process just is.

Benefits

  • No need to use rvm wrappers in every possible place such as cron, capistrano recipes etc.
  • No need to switch ruby versions because you just log in as proper user associated with the application and there is just the right ruby that you should be using with this app.

TL;DR

  • Every application has its own user
  • Every user has its own ruby

You might also like