The "durable stack" idea is easy to nod along to and harder to actually run. So here it is end to end: an empty folder to a live, authenticated app on one small server, with no Redis, no Node build pipeline, and no platform-as-a-service. Every step is a command you can paste.
We're using Rails 8 because, as of mid-2026, it ships the boring, low-maintenance defaults for free — you don't have to assemble them. Current stable is 8.1.3.
0. Prerequisites#
You need Ruby (3.2+) and Docker installed locally. Then:
gem install rails # installs the current stable (8.1.3)
rails --version # => Rails 8.1.3
1. Generate the app#
The default rails new already gives you SQLite, the Solid libraries, import maps, Hotwire, and Kamal. You don't need flags to get the durable stack — you'd need flags to opt out of it.
rails new blogmvp
cd blogmvp
What you just got, with no extra setup:
- SQLite as the database, tuned for production (WAL mode, immediate transactions).
- Solid Queue / Solid Cache / Solid Cable — background jobs, caching, and websockets, all on the database. No Redis.
- Import maps + Hotwire — JavaScript shipped to the browser directly. No npm, no webpack, no build step.
- Kamal — a
config/deploy.ymlready for a one-command deploy. - Propshaft — the modern, no-transpile asset pipeline.
2. Add real features (fast)#
Scaffold a resource. This generates the model, migration, controller, views, and routes:
bin/rails generate scaffold Post title:string body:text published:boolean
bin/rails db:migrate
Run it:
bin/rails server
Open http://localhost:3000/posts. That's a working CRUD app — create, edit, delete, all wired — in two commands.
3. Add authentication (built in now)#
Rails 8 added a first-class authentication generator. It's not a gem to configure; it scaffolds real, session-based auth you own and can edit.
bin/rails generate authentication
bin/rails db:migrate
This drops in a User and Session model, a SessionsController, password-reset flow (PasswordsController + mailer), and an Authentication concern you include in controllers. Add a before_action :require_authentication where you want to gate access, and you have login without a third-party dependency.
4. Background jobs, with no Redis#
Need to send an email or process something off the request cycle? Solid Queue is already the Active Job backend. Write a normal job:
bin/rails generate job PublishReminder
class PublishReminderJob < ApplicationJob
def perform(post)
# ...runs on Solid Queue, backed by SQLite. No Redis, no Sidekiq.
end
end
In development it runs inside Puma by default; in production Kamal runs the job worker for you. No separate queue service to stand up.
5. Deploy to one server#
This is where the "no PaaS" claim gets real. You need a plain Linux VPS (anything — a $5–$10 box works to start) and a container registry (Docker Hub or GitHub Container Registry).
Edit config/deploy.yml — set your image name, the server's IP, your registry, and a domain under proxy: for automatic SSL:
service: blogmvp
image: yourname/blogmvp
servers:
web:
- 203.0.113.10
proxy:
host: blog.yourdomain.com
ssl: true
registry:
username: yourname
password:
- KAMAL_REGISTRY_PASSWORD
Put your secrets in .kamal/secrets, then the first-time provision-and-deploy:
kamal setup
That builds your image, provisions the box, starts the app behind Kamal Proxy (which fetches a Let's Encrypt certificate automatically), and boots it. Every subsequent deploy is one command with zero downtime:
kamal deploy
The whole stack — app, jobs, cache, websockets, database, TLS — runs on that one machine. No managed database, no Redis instance, no build server.
The takeaway#
You now have an authenticated CRUD app with background jobs and a real deploy pipeline, running on a single cheap server, with no Redis and no JavaScript build step to maintain. That's not a toy — it's a genuinely production-shaped MVP with very few moving parts, which is exactly what makes it durable.
The honest limits, stated plainly: SQLite keeps you on one machine (a higher ceiling than most founders expect, but a ceiling), and you own that machine's patching and backups. If you already know you need Postgres, rails new blogmvp -d postgresql swaps only the database and leaves everything else identical.
The tool doing the deploy heavy-lifting here — Kamal — is worth understanding on its own; we cover it in a companion tool highlight. And for why founders are reaching for stacks like this in the first place, see The Durability Turn.



