Simplifying ActiveRecord Connection Strings
Jul 24, 2011
Over the years I've gone to great lengths to avoid committing production
passwords into version control. With Rails this has inevitably meant a
number of different ways to ensure the database.yml
was where it needed to
be on the production servers, with the right credentials. Custom Capistrano
recipes to create a new config when setting up a server, moving it into
place after a deploy, it worked well enough but it's always felt a little
heavy.
There's got to be a better way
There are a whole range of configuration settings that are environment
dependent. Which database to connect to, what queues Resque workers should
listen to, the Rack/Application environment mode. What we need is a
standard way on each server to set these environment variables. Oh hang on,
environment variables!
It's easy enough to either export an environment variable in a shell, or
pass it in with the command like RACK_ENV=production rackup config.ru
but a more complicated configuration like ActiveRecord requires doesn't
quite work like that. It's expecting a Hash that defines the database
adapter to use, username, password, the database, a host, and possibly a
bunch of other options. We could probably serialise that back and forth
between a Hash and something like JSON but it feels like a bit of a hack.
What would be better is if we could come up with a simple and uniform string
format that could define how to locate any resource, including a database. I
propose we call such a string a Uniform Resource Locator, or URL for short ;)
Using URLs to connect to databases
This isn't a new technique, other ORMs like Sequel have been doing this for
years. It's just that ActiveRecord hasn't supported it, until now. I've
created a gem that adds support for URL based connections to ActiveRecord,
it's called activerecord_url_connections.
Add it to your Gemfile
:
gem "activerecord_url_connections"
And now you can connect to your database by adding the following to an
initializer (e.g., config/initializers/activerecord.rb
):
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
Now you can either export the DATABASE_URL
environment variable in your
app environment or set it when you start your app like so:
DATABASE_URL=postgres://localhost/myapp_production
No more juggling database.yml
on production servers, no risk of checking
sensitive credentials into version control.
Convention Over Configuration
Something still doesn't feel quite right about this approach though. Creating
an initializer just to have that one line seems a bit needless, especially
when it will be exactly the same in almost every app. So in much the same
way Rails assumes a default connection to use if you don't provide one, so
it will look to see if you've set DATABASE_URL
and use it when available.
Stick with the conventions, and you'll not need to do anything beyond adding
the gem and setting DATABASE_URL
. Sweet!
But wait, there's more!
There's some further good news too. Firstly, for anyone using Heroku for
hosting Rack apps that use ActiveRecord this means connection to your
database will "just work" (it's not needed for Rails as Heroku create a
database.yml
file so the existing Rails behaviour will work). Secondly
this change has made it into ActiveRecord for the 3.2 release so you'll
only need this gem to backport the behaviour to previous releases.
Previously I led the Terraform product team @ HashiCorp, where we launched Terraform Cloud and set the stage for a successful IPO. Prior to that I was part of the Startup Team @ AWS, and earlier still an early employee @ Heroku. I've also invested in a couple of dozen early stage startups.