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.

Hi, I'm Glenn! 👋 I've spent most of my career working with or at startups. I'm currently the Director of Product @ Ockam where I'm helping developers build applications and systems that are secure-by-design. It's time we started securely connecting apps, not networks.

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.