GAURAV VARMA
Rails 6 brings first-class support for multiple databases, making it much easier to manage complex applications that need read/write separation, replica fallbacks, or domain-specific sharding — without relying on third-party gems.
Key Features
- Connect to multiple primary and replica databases
- Built-in connection switching middleware
- Independent migrations and schema tracking
- Out-of-the-box Rails tasks for each database
Setting Up database.yml
Here’s an example configuration with a primary DB and an animals
DB with replicas:
1production:
2 primary:
3 database: my_primary_database
4 adapter: mysql
5 user: root
6 primary_replica:
7 database: my_primary_database
8 adapter: mysql
9 user: readonly_user
10 replica: true
11
12 animals:
13 database: animals_db
14 adapter: mysql
15 user: animals_user
16 migrations_paths: db/animals_migrate
17 animals_replica:
18 database: animals_db
19 adapter: mysql
20 user: animals_readonly
21 replica: true
A few important things to note:
- Replicas must set
replica: true
- Use different users for primaries and replicas
- Use
migrations_paths
to isolate migrations per DB
Connecting Models to Databases
Create an abstract base model for each custom DB:
1class AnimalsBase < ApplicationRecord
2 self.abstract_class = true
3
4 connects_to database: { writing: :animals, reading: :animals_replica }
5end
For your default models:
1class ApplicationRecord < ActiveRecord::Base
2 self.abstract_class = true
3
4 connects_to database: { writing: :primary, reading: :primary_replica }
5end
If you're using legacy roles like :readonly
, you can override them in your config:
1config.active_record.writing_role = :default
2config.active_record.reading_role = :readonly
Rails Tasks for Multi-DB
Rails ships with namespaced commands for each database:
1rails db:create
2rails db:create:animals
3rails db:migrate
4rails db:migrate:animals
5rails db:migrate:status:animals
Each database uses its own migration directory. Use --database
when generating migrations:
1rails g migration CreateDogs name:string --database animals
Automatic Connection Switching
Rails includes middleware that automatically routes reads/writes:
- Writes (POST/PUT/DELETE) use the primary
- Reads (GET/HEAD) use the replica, unless a recent write occurred
Enable it in application.rb
:
1config.active_record.database_selector = { delay: 2.seconds }
2config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
3config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
You can even write your own resolver if you want to control switching based on cookies, headers, etc.
Manual Connection Switching
Use connected_to
if you want explicit control:
1ActiveRecord::Base.connected_to(role: :reading) do
2 # Read-only logic here
3end
Passing an unknown role will raise an error, so make sure it matches your config.
Caveats
- ❌ No native sharding support yet
- 🔀 No built-in replica load balancing
- 🚫 No cross-database joins
- 📦 You’ll need to manually load schema caches for each DB
Links
- Rails Multiple Databases Guide
- PR #36389 – Docs for multi-DB
- PR #34137 – Adds multi-db support to
db:migrate:status
- PR #34052 – Adds basic connection switching API
Summary
Rails 6 makes scaling your app’s data layer far more manageable with built-in multi-DB tools. Whether you're separating concerns across domains or using replicas to reduce load, Rails now gives you a clean and consistent interface to do it all natively.