Phusion white papers Phusion overview

Passing environment variables to Ruby from Phusion Passenger

By Hongli Lai on December 16th, 2008

Update June 4 2013: This article is completely obsolete. In Phusion Passenger 4, using SetEnv and PassEnv in Apache and env in Nginx works as expected. Detailed information can be found in the Phusion Passenger manual, section “About environment variables”.

Phusion Passenger manages Ruby/Rails process automatically. Sometimes it is necessary set environment variables or to pass environment variables to the Ruby interpreter. This particular aspect of Phusion Passenger isn’t very well documented, so it’s time for a blog post.

Environment variables that may be set after Ruby is started

Some environment variables may be set before or after Ruby is started. These include:

PATH
The search path for binaries.
LD_LIBRARY_PATH
The search path for shared libraries.

It really doesn’t matter where these environment variables are set, as long as you set them before you use them. These variables may be set in environment.rb or, if you’re using Apache, using the SetEnv directive.

Setting PATH, LD_LIBRARY_PATH and similar variables

Suppose that your environment.rb runs the program “frobnicate”, and this program is located in /opt/frobnicator/bin, which is not in PATH by default. Furthermore, the “frobnicate” program requires shared libraries which are located in /opt/awesome_runtime/lib. Suppose your environment.rb current looks like this:

...
Rails::Initializer.run do |config|
  ...
end
...
system("frobnicate")    # => ERROR: command not found

Set PATH just before the system() call so that it can find the program:

ENV['PATH'] = "#{ENV['PATH']}:/opt/frobnicator/bin"
system("frobnicate")    # => ERROR: cannot load libawesome_runtime.so

Now set LD_LIBRARY_PATH so that the program can find its libraries:

ENV['PATH'] = "#{ENV['PATH']}:/opt/frobnicator/bin"
ENV['LD_LIBRARY_PATH'] = "#{ENV['LD_LIBRARY_PATH']}:/opt/awesome_runtime/lib"
system("frobnicate")    # => success!

On Apache you can also use the SetEnv directive instead of hardcoding such settings your app:

# Outside any virtual host block:
SetEnv PATH /usr/bin:/usr/local/bin:/bin:/opt/frobnicator/bin
SetEnv LD_LIBRARY_PATH /opt/awesome_runtime/lib

Setting GEM_PATH, the RubyGems search path

If you’re on a shared host (e.g. Dreamhost) or on some other server for which you do not have root privileges, then you have no choice but to install gems to somewhere inside your home folder. You also need to tell RubyGems to look in there, and that’s what GEM_PATH is for.

Suppose that you’ve installed the gem “ruby-frobnicator” into /home/foobar/my_gems. In your environment.rb you must set GEM_PATH and call Gem.clear_paths just before requiring the gem, like this:

...
Rails::Initializer.run do |config|
  ...
end
...
ENV['GEM_PATH'] = "/home/foobar/my_gems:#{ENV['GEM_PATH']}"
Gem.clear_paths
require 'ruby-frobnicator'    # => it works!

Environment variables that must be set before Ruby is started

Some environment variables must be set before Ruby is started because the Ruby interpreter itself uses them. The RailsBench GC settings environment variables, which are now supported by Ruby Enterprise Edition, are examples of such environment variables.

You can set these environment variables by writing a wrapper script. Recall that Phusion Passenger has a “PassengerRuby” configuration option which typically looks like this:

PassengerRuby /usr/bin/ruby

You can point this to a wrapper script:

PassengerRuby /usr/local/my_ruby_wrapper_script

/usr/local/my_ruby_wrapper_script can set the environment variables prior to executing the real Ruby interpreter:

#!/bin/sh
export RUBY_HEAP_MIN_SLOTS=10000
export RUBY_HEAP_SLOTS_INCREMENT=10000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1.8
export RUBY_GC_MALLOC_LIMIT=8000000
export RUBY_HEAP_FREE_MIN=4096
exec "/usr/bin/ruby" "$@"

A few notes for those who are not familiar with writing shell scripts:

  • Make sure you make /usr/local/my_ruby_wrapper_script executable with chmod +x.
  • Make sure that you prepend the “export” keyword to all environment variable setter statements.
  • The last line says “replace the current process with /usr/bin/ruby, and pass all commandline argument that I’ve received to Ruby”. Make sure that $@ is wrapped inside double quotes, otherwise filenames with spaces in them won’t be passed correctly to the Ruby interpreter.

But wait, I’ve already set environment variables in my /etc/bashrc or /etc/profile. Why don’t they work?

If you’ve set environment variables in your /etc/bashrc or /etc/profile, then these environment variables are made available in your shell. However, on most operating systems, Apache is not started from the shell and does not load environment variables defined in bashrc/profile, which is why setting environment variables in /etc/bashrc and /etc/profile usually has no effect on Apache (and by induction, on Passenger and Rails processes).

Final words

This is just a quick blog post which I’ve written after seeing many people asking questions on this subject. This subject deserves proper official documentation, but I haven’t had the time to do it yet. If anybody wants to submit a documentation patch then please feel free to do so. In the long term it would probably be nice if one can pass environment variables to the Ruby interpreter via Apache configuration options, but it’s not a very high priority issue at this moment.

  • Rhett

    Thanks for the useful, linkable clarification. One additional note: sometimes, LD_LIBRARY_PATH falls into the category of environment variables which are used by the interpreter itself. (One example of this is when you are using the ruby-oci8 driver to talk to an oracle database in your rails app.) In this case, you’ll need to use the wrapper script method to set LD_LIBRARY_PATH.

  • Luke Redpath

    I know there is an open ticket on this, but it would be nice if you could set environment variables using mod_env’s SetEnv directive instead.

  • Per Velschow

    If you are on a shared host, you cannot change the PassengerRuby configuration option. Is there any other way to control which version of Ruby and more importantly RubyGems to be used? Or are you stuck on the versions installed by the host?

    Since you mention DreamHost, I can say that they currently (on my server at least) have a very old Ruby installed: 1.8.5 (2006-08-25) and until recently also an outdated version of RubyGems. An ability to control these from a .htaccess file would be much appreciated.

  • http://www.phusion.nl/ hongli

    @Per: No, it’s not possible.

  • http://www.dcmanges.com Dan Manges

    mod_env’s SetEnv works for me. I’m using this in my Apache config: SetEnv RAILS_RELATIVE_URL_ROOT /v1

  • Andy

    What a mess!! Come on phusion, you can do better than this!!!

  • http://www.phusion.nl/ hongli

    @Andy: There are many things on our todo list which have higher priority than fixing the perceived messiness of setting environment variables. We’ll get there eventually but not right now. If you wish that this particular issue is addressed more quickly, then we’d be more than happy to accept a patch or to discuss a sponsorship campaign with you.

  • http://www.dreamhost.com/ Dallas

    @Per : DreamHost is in the process of upgrading to Ruby 1.8.7. If you contact our support team we can roll that out on your server.

    Also, we will probably be adding a way for you to control the PassengerRuby setting on our DreamHost PS servers soon. Also contact our support team if that’s something you are interested in.

  • http://blog.rayapps.com Raimonds

    I was using the same solution as recommended here. But now I am trying a little bit cleaner approach.

    If you have root access to server then in Linux case you can modify envvars file (see apachectl source to find file location on your system) to include environment variables that should be set up before Apache is started.

    And in Mac OS X you can modify file /System/Library/LaunchDaemons/org.apache.httpd.plist to include EnvironmentVariables entry with dictionary of additional environment variables.

  • Pingback: passenger and rmagick on shared hosting « dimas priyanto()

  • Pingback: … So We Built One :: ImageScience, FreeImage, and Dreamhost PS()

  • http://www.jango.com daniel

    If you are redhat/centos/fedora user, you can add the lines to /etc/sysconfig/httpd. I’m sure other OSes have similar functionality. You could also add it directly to init scripts, but they have the potential to be overwritten with package updates.

  • http://www.sandsfish.com Sands Fish

    FYI for all of you Dreamhost users out there, for some cases, some compile-time magic can remove the necessity of setting LD_LIBRARY_PATH during run-time.

    In my particular case, it was the taf2-curb gem (cURL bindings for ruby) that refused to work. It would compile fine if I passed it the location of my custom install of the curl libs (i.e. “gem install taf2-curb — –with-curl-dir=/my/path/to/custom/libcurl”) but when I went to use it, it couldn’t find the curl libraries.

    I spent hours trying to set LD_LIBRARY_PATH via SetEnv directives, environment.rb entries, etc. etc. but what fixed it was this:

    http://discussion.dreamhost.com/showthreaded.pl?Cat=0&Board=forum_programming&Number=88041&page=0&view=&sb=&part=&vc=1&o=

    A simple setting of LD_RUN_PATH before doing the gem compile/install mentioned above (“export LD_RUN_PATH=/my/path/to/custom/libcurl”) fixed the location of the libraries for the gem and it “Just Works” (after hours of misery).

    My guess is that it would work for anything else (i.e. the ruby-oci8 driver, etc.) if you can build it yourself.

  • http:/kozgun.net Ozgun Koyun

    Dear “Sands Fish”,

    You saved the day! Thanks a lot! I’ve been trying to install “taf2-curb” on a dreamhost machine, finally I managed to install it. I set the “LD_RUN_PATH” and “LD_LIBRARY_PATH” with export, and also added to .bash_profile and .bashrc. Then, complied the taf2-curb, and copied “curb_core.so” from ext directory to the “lib” folder of taf2-curb gem.

  • Pingback: almost effortless » Weekly Digest, 11-6-09()

  • Pingback: ImageMagick 和 Rmagick 之灵异事件 - 噏 【Up】()

  • Pingback: passenger and rmagick on shared hosting « dimas priyanto()

  • http://www.optinnow.org Jonathan Neddenriep

    For any of you struggling with the Ruby 1.9.1 lack of support for passing the GEM_PATH in the environment variable, if you use the wrapper method outlined above it works.

  • Pingback: Ruby on Rails Shared Hosting Gem Issue()

  • Pingback: Rails ENV when using Phusion Passenger « Only The Wind Knows()

  • Noah

    FWIW: I wasn’t able to use mod_env to modify PATH using SetEnv. According to docs here: http://httpd.apache.org/docs/current/env.html it looks like PATH might get cleaned after mod_env.

  • http://railsgeek.com mikhailov

    Ruby GC tuning run my specs 1.5-2times faster. see Basecamp, Twitter and default settings. Use ruby enterprise to test https://gist.github.com/865706

  • Pingback: Visoft, Inc. Blogs | Ruby on Rails Shared Hosting Gem Issue()

  • Pingback: Ruby Garbage Collector Performance Tuning | Carpe Diem()

  • Pingback: rails3 unbearably slow view rendering: use REE with GC tuning | Bibliographic Wilderness()

  • http://bibwild.wordpress.com Jonathan Rochkind

    I don’t think this has been documented anywhere else better than this blog post?

    Regarding REE GC tuning env, it looks like recent versions of passenger under RVM install using a wrapper with an ‘environment’ file already. For instance, wrapper (included as PassengerRuby in passenger apache output) at:

    /usr/local/rvm/wrappers/ree-1.8.7-2011.03/ruby

    and that script already sources a bash file setting ENV at:

    /usr/local/rvm/environments/ree-1.8.7-2011.03

    So i think REE GC tuning env can conveniently be put in that ../environments/… file? Not sure if ‘export’ is needed, I think it’s not, but I used it anyway just to be safe.

    Some updated documentation on this stuff would be awesome.

  • Pingback: Tales from upgrading to Ruby 1.9.2 - character encoding()

  • Pingback: Installing phusion passenger with apache2 on Ubuntu « Exploring myself…()

  • lzap

    The PassengerRuby can be also used for SELinux trantition into proper application context I guess.