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:

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.