Solid Trifecta + Kamal + Postgres don’t play along nice, this is the way to fix it

When you deploy a fresh Rails 8 app with the Solid “trifecta” (Solid Cache, Solid Queue, Solid Cable), you actually have four database connections in production: primary, cache, queue, and cable. If only `DATABASE_URL` is set, the other three try to connect via the default local socket and you’ll see:

bin/rails aborted!
ActiveRecord::ConnectionNotEstablished: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
...
Tasks: TOP => db:prepare

The fix is to explicitly configure all Solid DBs and run their migrations.

1) Wire up all four database URLs

Use distinct URLs, or point them all to the same Postgres if that’s fine for your scale.

config/database.yml (production)

production:
  primary:
    url: <%= ENV["PRIMARY_DATABASE_URL"] || ENV["DATABASE_URL"] %>

  cache:
    url: <%= ENV["CACHE_DATABASE_URL"] || ENV["DATABASE_URL"] %>
    migrations_paths: db/cache_migrate

  queue:
    url: <%= ENV["QUEUE_DATABASE_URL"] || ENV["DATABASE_URL"] %>
    migrations_paths: db/queue_migrate

  cable:
    url: <%= ENV["CABLE_DATABASE_URL"] || ENV["DATABASE_URL"] %>

Rails 8 treats cache/queue/cable as separate DB configs; providing CACHE_DATABASE_URL, QUEUE_DATABASE_URL, and CABLE_DATABASE_URL (or falling back to DATABASE_URL) prevents the “local socket” miss.

Kamal deploy.yml snippet:

env:
  clear:
    RAILS_ENV: production
  secret:
    - DATABASE_URL
    - PRIMARY_DATABASE_URL
    - CACHE_DATABASE_URL
    - QUEUE_DATABASE_URL
    - CABLE_DATABASE_URL

(If you’re running a Postgres accessory with Kamal, ensure these URLs point at that service, not a local socket.)

2) Install Solid components (if not already)

These tasks create the right schema/migration paths.

bin/rails solid_cache:install
bin/rails solid_queue:install
bin/rails solid_cable:install
  • Cache/Queue create migration paths under db/cache_migrate and db/queue_migrate.
  • Cable creates db/cable_schema.rb and requires a DB entry.

3) Commit a migration for cache/queue (don’t rely only on schema dumps)

Add an explicit migration under each path so db:prepare can apply them in production:

bin/rails g migration InitSolidCache --migration_path=db/cache_migrate
bin/rails g migration InitSolidQueue --migration_path=db/queue_migrate

Paste the table definitions from your local generated schema files (or re-run the install tasks locally and copy the structure). Then:

bin/rails db:prepare

This guarantees the cache and queue tables exist in the target DB(s), just like your primary.

4) (Option) Keep it simple: one Postgres for all four

For small apps, it’s perfectly fine to point primary, cache, queue, and cable to the same Postgres and split them out later as load grows. Update the four entries in database.yml to all use ENV["DATABASE_URL"].

Why this happens

Rails 8 defaults to multi-DB for Solid Cache/Queue/Cable. PaaS/Kamal setups commonly only export DATABASE_URL. Without explicit URLs, the Solid DBs fall back to a local socket path (/var/run/postgresql/...), which doesn’t exist in your container, hence ConnectionNotEstablished. Setting *_DATABASE_URL (or wiring database.yml to reuse DATABASE_URL) aligns all four connections with your actual Postgres.

Quick Checklist (made by AI)

[ ] database.yml has primary/cache/queue/cable with URLs
[ ] Env has CACHE_DATABASE_URL, QUEUE_DATABASE_URL, CABLE_DATABASE_URL (or they fall back to DATABASE_URL)
[ ] Ran solid_cache:install, solid_queue:install, solid_cable:install
[ ] Created/committed migrations under db/cache_migrate and db/queue_migrate * [ ] bin/rails db:prepare succeeds in the deploy container

After this, Kamal deploys should boot cleanly without the Postgres socket error, and Hotwire/Action Cable plus jobs and cache will work as expected.