Solid Queue, Solid Cache, Solid Cable

Rails 8 introduced three database-backed backends — Solid Queue, Solid Cache and Solid Cable — that collectively let you run a production Rails application without Redis, without Memcached, and (for smaller apps) without any extra infrastructure at all. The Rails team refers to them as the "Solid trifecta".

Each gem is independent but they share a philosophy: use the database you already have. A small-to-mid-sized app can ship with nothing more than a Rails container and a PostgreSQL (or SQLite) database.

A fresh rails new myapp wires up all three.

Solid Queue

Solid Queue is a production-grade Active Job adapter. It persists jobs in the database, supports schedules (cron-style recurring jobs), concurrency controls, and multiple worker queues.

Configuration lives in two files:

  • config/queue.yml — worker/dispatcher sizing.

  • config/recurring.yml — cron-style schedules.

Jobs are scheduled the usual way:

ExampleJob.perform_later(user)
ExampleJob.set(wait: 1.hour).perform_later(user)

In production you run a worker process:

$ bin/jobs

Kamal’s generated config/deploy.yml already defines jobs as a separate service that runs bin/jobs.

See Active Job for the full chapter.

Solid Cache

Solid Cache is the default Rails.cache backend. It stores entries in a dedicated database (default: a separate SQLite file for development, PostgreSQL for production) and is optimized for write-heavy workloads with SSDs. The writers are fire-and-forget, so cache writes don’t slow down requests.

Use it exactly like any other Rails cache:

Rails.cache.write("stats:today", counts, expires_in: 5.minutes)
Rails.cache.fetch("user:#{user.id}:friends") { user.friends.to_a }
Rails.cache.delete_matched("homepage:*")

Solid Cache is configured automatically in config/environments/production.rb by rails new:

config.cache_store = :solid_cache_store

config/cache.yml controls the max age (default 60 days) and max size (default 256 MB). Because the cache lives in a regular SQLite (or PostgreSQL / MySQL) database, you can also poke at it with plain SQL:

$ bin/rails dbconsole -e production
sqlite> SELECT COUNT(*) FROM solid_cache_entries;
sqlite> SELECT key FROM solid_cache_entries LIMIT 5;

See Caching for patterns that get the most out of it.

Solid Cable

Solid Cable is the default Action Cable adapter. It pipes WebSocket messages through the database rather than through Redis Pub/Sub, so you can run real-time updates without a Redis server.

It’s configured in config/cable.yml:

default: &default
  adapter: solid_cable
  connects_to:
    database:
      writing: cable
  polling_interval: 0.1.seconds
  message_retention: 1.day

development:
  <<: *default

test:
  adapter: test

production:
  <<: *default

The polling_interval of 100ms is a compromise between latency and database load. For most chat-style applications it’s plenty.

See Action Cable and the Turbo Streams examples in Hotwire for the client side.

When Not to Use Them

The Solid trifecta is a great default. It simplifies your infrastructure story and removes entire categories of bugs ("Redis was down but Postgres was fine, so the app accepted writes but dropped every background job"). That said:

  • Extremely high-throughput background jobs — if you are running tens of thousands of jobs per minute, Sidekiq on Redis is still faster than Solid Queue on Postgres. Start with Solid Queue and switch if you actually hit a limit.

  • Very high-frequency cache writes — Solid Cache handles thousands of writes per second, but millions per second is what Redis is for. For a CMS, marketplace, or SaaS the defaults are plenty.

  • Multi-region / globally distributed apps — a single Postgres instance is a single point of failure. Consider a Redis cluster or a global cache (Cloudflare Cache, etc.) for that case.

For the other 99% of Rails apps the Solid trifecta is the right choice.

Further Reading