Rails Database Connection Behavior
Last updated December 02, 2019
Table of Contents
This article describes how various Active Record versions can be configured to reference a particular database.
Rails 4.1 and beyond
While support for reading from DATABASE_URL
has been in Rails for some time, there were always edge cases that prevented Heroku from relying on Rails core behavior until Rails 4.1. Introduced in this pull request if you have ENV["DATABASE_URL"]
set on your system it will be used as canonical connection information regardless of what RAILS_ENV
environment you are running in. The idea is that even if you’ve set up your local machine to run in production mode, you might have committed connection information to your config/database.yml
file:
production:
database: example_production
adapter: postgresql
host: localhost
password:
While this might work locally, Heroku runs a Postgres server on a different machine, so it would not work if your app tried to connect to localhost
with a blank password when you deployed to Heroku. By always connecting to ENV['DATABASE_URL']
when it is present, such problems are non-existent.
Configuring connections in Rails 4.1+
While the default connection information will be pulled from DATABASE_URL
, any additional configuration options in your config/database.yml
will be merged in. The idea is that DATABASE_URL
only holds connection information, but not behavior information. You can still configure Active Record behavior such as pool
setting:
production:
encoding: utf8
pool: 15
You cannot use the config/database.yml
to set any values found in ENV['DATABASE_URL']
. This is a list of attributes you cannot change:
- adapter
- database
- username
- password
- host
- port
Any other attributes though will work. You can verify this by running:
$ heroku run console
> config = ActiveRecord::Base.configurations[Rails.env] ||
Rails.application.config.database_configuration[Rails.env]
> puts config
# => { :pool => 15, # ... }
Active Record 4.1+ Escape Valve
If you need need to connect to a different database than the ENV['DATABASE_URL']
Rails 4.1+ supports a URL key in the database.yml
file that will take precedence.
production:
url: <%= ENV["SOME_OTHER_URL"] %>
When you do this, no information from DATABASE_URL
will be merged in, it is assumed that by being explicit with the URL you want to connect to, all implicit behaviors should be skipped. You could use this to modify information in the DATABASE_URL
, for example to specify PostGIS as an adapter:
production:
url: <%= ENV.fetch('DATABASE_URL', '').sub(/^postgres/, "postgis") %>
When using a URL key, you can also pass in non-connection information that will be merged in such as pool
:
production:
url: <%= ENV["SOME_OTHER_URL"] %>
pool: 15
The behavior is identical to what is described above. The data in url
will take precedence, though anything else will be merged in.
Prior to Rails 4.1
Before Rails 4.1 (Active Record 4.1) the only way to correctly configure the connection to a database was to use the config/database.yml
file. Instead of asking customers to pull down their connection information and commit it to source, Heroku instead injected its own database.yml file that would read from the ENV["DATABASE_URL"]
. If you are using a version of Rails prior to 4.1 then this is still applicable. You can see this file by running:
$ heroku run bash
$ cat config/database.yml
If you need to configure your database connection, it is difficult because nothing you commit to your database.yml
locally will be used in production (since we write over the file). Instead there are ways to use an initializer to configure database behavior.
While this was a simple approach, it caused problems as it wasn’t entirely clear what we were doing, which lead to edge case problems. Instead of “magically” writing over a file, in Rails 4.1+ we switched to using functionality baked into Rails directly.