DHH announced Rails 8 adding a lot of support for SQLite at Rails World 2024, which motivated me to give it a shot. I enthusiastically procrastinated this quest. One year later, after visiting Rails World 2025 and realizing this whole SQLite thing was here to stay, I got down to writing some code.
I migrated one of our Rails 7 apps to Rails 8, migrated from Postgres to SQLite, and migrated to the solid {queue,cache,cable} stack, which is based off SQLite, too.
I was initially skeptical because, I use dokku for deploying my Rails apps, and dokku is magic, therefore the deployment experience is magical. I assumed that now juggling four (!) SQLite databases would feel less magical.
But the TLDR is that my assumption was wrong – Rails 8 + SQLite is cool.
So. Here’s how a typical new Rails deployment looks with dokku:
dokku apps:create rails_app
dokku postgres:create rails_app_pg
dokku postgres:link rails_app_pg rails_app
dokku redis:create rails_app_redis
dokku redis:link rails_app_redis rails_app
dokku memcached:create rails_app_memcached
dokku memcached:link rails_app_memcached rails_app
Done. This feels sufficiently easy, and it can be summarized as the “Heroku deployment experience” which made Heroku so popular, back in the stone age when the Heroku founders were still at the company.
Compare this with the SQLite-based solid stack deployment:
dokku apps:create rails_app
dokku storage:ensure-directory rails_app
dokku storage:mount /var/lib/dokku/...:/app/storage
So that looks simpler. But it’s not that simple: You’re now juggling four (!) SQLite databases, migrations and schemas:
- Your actual db (
db/migrate
anddb/schema.rb
) - Your solid queue db (
db/queue_migrate
anddb/queue_schema.rb
) - Your solid cache db (
db/cache_migrate
anddb/cache_schema.rb
) - Your solid cable db (
db/cable_migrate
anddb/cable_schema.rb
)
My knee-jerk reaction to this was “dude, this is hell!”. Sure, we’ve replaced Postgres + Redis + Memcached, but at the cost of introducing four SQLite databases, each with their own complexity, plus having to configure out deployment so that those four databases are actually persisted correctly (easy on dokku, hard on Heroku).
But is it really hell?
After a few days playing around with this stack, I’ve changed my opinion. It’s pretty neat. Interestingly, I only slowly discovered the benefits over time, so judging it by its looks (four databases!) was not sufficient.
Here are my thoughts:
Postgres has its own hidden complexity: Major Postgres version upgrades require some devops work – in dokku, the easiest solution is to dump your database, upgrade your Postgres major version, then re-import the dump. Not hard, but it imposes a small mental “devops tax” which becomes relevant if you’re a solo developer or part of a small team.
Juggling three different services has hidden complexity, too: I’m no expert in Redis and Memcached. On rare occasions, I had to connect to the Redis instance correctly to look at its data. Man, what a pain. Redis is great (at least that’s what people say), but having to re-read the docs and “query language” every time I interact with it is not a great experience. And it gets worse: How would I even go about e.g. a major Redis version upgrade? Maybe its easy, maybe its not. But the point is that I’d have to dig into the docs (again) to ensure that e.g. the data gets migrated correctly to e.g. ensure that no background jobs get lost (Sidekiq etc.).
SQLite performance is surprisingly amazing: One aspect which people forget is that, usually, communication between the Rails app and Postgres server happens via TCP, i.e. you’re sending (local) network requests to your Postgres instance. This isn’t particularly bad, but it’s not an extremely fast solution if you’re mainly planning a single-server deployment. SQLite, on the other hand, is blazingly fast and essentially optimized for a single-server deployment. I don’t have real numbers here except that the dev experience feels slightly more “snappy”: Dropping and re-creating the database is super fast, (re-)running migrations is pretty much instant, etc., etc. Talking about the dev experience..
Development setup got radically simpler: One of the unexpected benefits is that I can now completely skip Docker for development. That’s crazy! In the past, I used what I call “the poor man’s docker dev setup” by just spinning up e.g. a Postgres instance like so:
docker run --name postgres -p 5432:5432 -d -e POSTGRES_PASSWORD=postgres postgres:17
And then, in my Rails dev config, I’d simply point it to the Postgres server on localhost:5432
, done. I’d do that for Redis and Memcached, too. Cool.
The crazy thing now with the “SQLite Stack” is that I now can skip Docker entirely. That feels awesome.
SQLite invites you to peek inside: With Postgres, I always felt like I had to jump through some hoops to look at the actual data – I either had to use the Rails console and execute something like MyModel.count
and MyModel.all
or so, or I had to open up the Postgres console (through Docker!) and write some SELECT
queries which hopefully would work. This becomes the most apparent when I wanted to figure out how large a particular Postgres database actually was – not a trivial task, because the files are stashed away somewhere in a Docker container, and there are multiple files, too, so I always have to head to Stackoverflow and find the Magic Query Incantation which gives me this. Here it is, by the way:
select t1.datname AS db_name,
pg_size_pretty(pg_database_size(t1.datname)) as db_size
from pg_database t1
order by pg_database_size(t1.datname) desc;
SQLite feels different. It invites you to peek inside! Want to see how large your database is? Just take a look at the (one!) file and its size. Want to look inside the database? There are beginner-friendly UI-based tools like DB Browser for SQLite which make SQLite databases feel almost as accessible as.. spreadsheets. I think all of that is awesome.
Rails 8 Feels a Bit Like Clojure
For me, building apps in Rails 8 nowadays feels similar like coding in Clojure back in the days. Clojure, like probably all lisps, gives you the feeling of control: Because of the lack of “big” libraries, you usually end up building your application from the ground up by assembling many small libraries you control. I remember writing a browser-based medical image viewer from scratch and damn, I was almost doing everything, like literally low-level re-calculations of pixel values when users changed the brightness and contrast settings. It felt awesome. Because not only was the whole code base cached in my head, the whole “application surface” was cached in my head, too – if there was some bug with the rendering of images, the problem likely was in my code, not in someone’s library code I was using somewhere. So the fix boiled down to fixing some code, and not scrolling through someone else’s library docs.
While Rails 8, by the nature of being a large framework, still abstracts away a lot of the application surface from you as a developer, I feel that the recent push towards the SQLite Stack has given each Rails developer a significantly larger chunk of the surface they feel they control.
Rails 8 with SQLite is awesome – you should try it.
Leave a Reply