Rails 8 beta apps currently don’t deploy well out of the box with PaaS providers which provision a DATABASE_URL
, like Fly.io and Heroku. This is because Rails 8 beta apps have up to 4 databases by default in production: primary
, cache
, queue
, and cable
.
Playing around with database.yml
Assuming I have a DATABASE_URL
that looks like this: postgres://user:password@host:port/database_name
, and assuming the other database names are database_name_cache
, database_name_queue
, etc, I had the following setup for Kamal deploys:
production:
primary: &primary_production
<<: *default
url: <%= ENV['DATABASE_URL'] %>
cache:
<<: *primary_production
url: <%= ENV['DATABASE_URL']&.+('_cache') %>
migrations_paths: db/cache_migrate
queue:
<<: *primary_production
url: <%= ENV['DATABASE_URL']&.+('_queue') %>
migrations_paths: db/queue_migrate
...
Then I realized that this setup didn’t work with Fly.io because they use query parameters inside the database URLs, like postgres://user:password@host:port/database_name?sslmode=disable
. So for a more general setup I am currently using the following:
production:
primary: &primary_production
<<: *default
url: <%= ENV['DATABASE_URL'] %>
cache:
<<: *primary_production
url: <%= URI.parse(ENV['DATABASE_URL']).tap { |u| u.path += '_cache' } if ENV['DATABASE_URL'] %>
migrations_paths: db/cache_migrate
queue:
<<: *primary_production
url: <%= URI.parse(ENV['DATABASE_URL']).tap { |u| u.path += '_queue' } if ENV['DATABASE_URL'] %>
migrations_paths: db/queue_migrate
...
This deploys great with both Kamal and Fly.io. Heroku is another story though, it doesn’t work out of the box because they don’t seem to allow creating multiple databases within the same database add-on. I am setting aside this concern for now.
In any case, the yaml setup above looks a little too verbose in order to support Rails 8 defaults with PaaS providers. It is unclear to me in these early days if Rails should have slightly more streamlined configuration, or PaaS providers should adapt, or a little bit of both.
Finding ways to simplify the database.yml setup
I am assuming that DATABASE_URL
represents the primary database, which seems to be the case according to the source code and the Heroku docs.
-
A solution could be to use
DATABASE_URL
as the base to define URLs for Solid databases.Maybe something like this PR from Sam Ruby.
Or since traditionally
ENV['DATABASE_URL']
has more precedence thandatabase:
, maybe introduce a higher precedence field likeurl_database
(for lack of a better name):production: primary: &primary_production <<: *default url: <%= ENV['DATABASE_URL'] %> cache: <<: *primary_production url_database: myapp_production_cache ... queue: <<: *primary_production url_database: myapp_production_queue ...
where
url_database
would overrideDATABASE_URL
’s database name portion while still using other values like user/password/host/port. -
According to this code, instead of complicating the database.yml setup, one could set the
(PRIMARY_)DATABASE_URL
,CACHE_DATABASE_URL
,QUEUE_DATABASE_URL
, andCABLE_DATABASE_URL
variables. But if you want to set those URLs based on the value of(PRIMARY_)DATABASE_URL
, you sometimes can’t do that before the first deploy because some PaaS providers like Fly.io don’t give you access to that variable (maybe there is a way to reference another variable when setting it). What would be best IMO is if the PaaS provider could populate all these variables automatically so it is seamless for the user.
I am curious about the Rails team’s thoughts on this topic and whether new PRs are needed to make things smoother.