Passing environment variables to Ruby from Phusion Passenger
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.