Last Updated: October 29, 2022
·
22.16K
· felixyz

Pass the database pwd to Rails with an environment variable

Storing your database password in database.yml is bad for several reasons. Let's pass it to Rails through an environment variable instead! (Using Apache and Passenger.)

Background

The way Heroku handles various sensitive keys, like the database URL, is to store them in environment variables. You can set environment variables from the command line using the heroku command line tool:

heroku config:add S3_KEY=<key_string>

This practice is also one of the Twelve Factors (www.12factor.net/config).

Ok, how can you make this work if you're hosting your app on your own server?

database.yml

Just replace the password string with a reference to the environment variable we're going to set up:

<database.yml>
production:
  ...
 password: <%= ENV['RAILS_DB_PWD'] %>

Server side

Making the environment variable accesible to your Rails app is slightly trickier. The solution will vary depending on your server setup, but here I'll assume you're using Apache + Phusion Passenger, but the same trick should work for most setups with minimal tweaks.

You can't just set the environment variable in a shell profile, because that won't be loaded when Passenger is started. Instead, you create a wrapper script (ruby_wrapper) that gets called when Passenger, and set the variables there.

But first, let's create a separate script that actually sets the variables (you'll see why soon):

<set_environment>
export RAILS_DB_PWD=<password here>

Next, we create the wrapper and source set_environment from within it:

<ruby_wrapper>
#!/bin/sh
source /path/to/set_environment
exec "/usr/local/bin/ruby" "$@"

(Make sure the last line is right for your system. Run "which ruby" if in doubt.) I like to keep this file right next to the root of my Rails app. The file must be executable:

chmod +x ruby_wrapper

Now you can instruct Passenger to use this wrapper script when starting a Ruby process, by changing a line in your httpd.conf file like so:

#PassengerRuby /usr/local/bin/ruby
PassengerRuby /path/to/ruby_wrapper

Shell access (and Capistrano)

At this point, if you try to run rake commands on your server through a shell, they will fail. Why? Because you've only told Passenger to use your ruby wrapper, but not the rest of the system. We need to export the same variables into any process where rake or Rails will run. This is done in a shell startup file, usually (on Linux systems) under /etc/profile.d. Create a new script there:

</etc/profile.d/my_profile.sh>
source /path/to/set_environment

Presto! Now rake commands will work from the command line. Also, running Capistrano deployments and the like will work, because the same startup files will be loaded when those processes are created. (Setting the environment in ~/.bash_profile wouldn't work in those cases, however.)

Now you can put anything sensitive in your set_environment file, which means you don't have to check it into your SCM (like git). You can also get fancier and fetch your environment variables from an yml file, or an sqlite database, or a central server... The point is to keep this completely separate from the Rails app itself.

This is a good practice for configuration that isn't sensitive too, because it lets you tailor each server instance any way you want without having to create new groups in your database.yml file or other places.

2 Responses
Add your response

Very good tip i wiil try it

Thanks

over 1 year ago ·

Just a heads up to others reading this that this line is no longer accurate, you can in fact do that now with Passenger:

You can't just set the environment variable in a shell profile, because that won't be loaded when Passenger is started

over 1 year ago ·