Rails Apps on V8 Isolates
Running a Ruby on Rails application on a V8 Isolate sounds absurd. V8 runs JavaScript, not Ruby. Rails expects a long-running server, not ephemeral functions. The whole idea seems backwards.
And yet.
The JavaScript ecosystem is delightfully weird. Nobody writes JavaScript anymore—they write TypeScript, JSX, Svelte, or a dozen other languages that transpile to JavaScript. Bundlers reshape applications into entrypoints. React Server Components use "use server" directives that do nothing except signal the bundler to transform the code.
What if we could do the same with Ruby? Not run Ruby itself, but transpile Ruby applications into JavaScript and reshape them into functions?
That's the vision: full-stack Rails applications running on Vercel Edge, Cloudflare Workers, or Deno Deploy.
Why V8 Isolates?
First, some context on where applications run.
Traditional hosting means a server that's always on—a VPS, a container, a Kamal deployment. You pay for capacity whether you're using it or not. This makes sense if you're large and your traffic is predictable.
But if you're small or your traffic is spiky, you're either overprovisioned (paying for idle capacity) or underprovisioned (slow when it matters). V8 Isolates offer a different model.
V8 Isolates power Cloudflare Workers, Vercel Edge, and Deno Deploy. They're lightweight JavaScript execution environments that spin up on demand:
- Instant startup — under 5ms cold starts
- Global distribution — code runs at the edge, close to users
- Ultra-low overhead — thousands of isolates per machine
- Pay for what you use — idle apps cost nearly nothing
For a Rails developer, this means: deploy your app once, have it run globally, scale up when needed, and pay almost nothing when it's idle. No capacity planning. No configuring load balancers on Hetzner or Digital Ocean. No worrying about which region to deploy to.
The question is: can we get there?
Why Rails?
Rails is surprisingly well-suited for this transformation.
The core is MVC, but what makes Rails special is how declaratively it's expressed. Routes, associations, validations, callbacks—these aren't imperative code, they're declarations. Convention over configuration means the architecture is implicit in the file structure. Then you add small bits of imperative code: a validation here, a loop there, a controller action.
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true
end
This isn't a program to execute—it's a specification to transform.
Beyond the core, Rails has abstract interfaces: Active Job, Action Mailer, Active Storage, Action Cable. Each represents a proven pattern with cloud-hosted alternatives—SendGrid for email, S3 for storage, Pusher for real-time. These abstractions define what your application needs, not how it's implemented.
Declarative structure. Abstract interfaces. Small scripts. All things that an "npm run build" process could use as inputs.
Demo Time
My previous post showed the same Rails blog running on five platforms. But CRUD is the easy part. Real applications need real-time updates—and that's where things get interesting.
Introducing the Juntos Chat Demo. A simple chat application—Stimulus controller, Turbo Streams, WebSocket—that runs unchanged in Rails with Action Cable, in the browser with BroadcastChannel, on Node.js with WebSockets, and on Cloudflare with Durable Objects.
Here's the chat's Stimulus controller, written in Ruby:
class ChatController < Stimulus::Controller
# Auto-scroll to show the new message
def messageTargetConnected(element)
element.scrollIntoView()
end
# Clear the message input after form submission
def clearInput
bodyTarget.value = ""
bodyTarget.focus()
end
end
Familiar Ruby syntax. Direct and seamless access to JavaScript objects. The documentation includes live demos where you can edit Ruby code and see it transpile in real time.
Is This Just Demoware?
Honestly, yes for some parts, no for others.
The Ruby2JS base is solid. Ruby and JavaScript compatibility is a spectrum—Opal represents one end with full method_missing support, while Ruby2JS lets you choose where you want to be, with intelligent defaults that work for most applications.
The Rails filters are new and have gaps. Routes and ERB work well. The model filter is minimal—just enough to support the demos—but Rails' Arel provides a design to follow. Controllers are somewhere in the middle.
No showstoppers. Just work to be done.
What's Next
Edge deployment needs caching. A DSL for revalidation times and cache tags—no-ops in traditional Rails, but generating proper headers for edge targets.
Next, Vite and Phlex look like quick wins.
A Vite plugin would bring modern developer experience to Ruby: hot module replacement, source maps showing Ruby in DevTools, production builds with tree shaking. Edit a Stimulus controller, see the change instantly.
Phlex—pure Ruby view components—opens the door to component-based architecture. Since Ruby2JS can already target React, and ERB is essentially EJS with Ruby, perhaps multiple component formats can be first-class citizens.
And since we're transforming code for both client and server, why not use :client and use :server directives? Ruby's answer to React Server Components.
Hybrid Use Cases
While taking Rails to places it can't currently reach is Juntos' raison d'être, the hybrid use cases are interesting on their own.
Here's a practical one: you want to deploy to Cloudflare Workers with D1, but you don't want to run Wrangler and hit remote databases during development. The solution is the same config/database.yml pattern Rails developers already know—SQLite locally, D1 in production. Same SQL dialect, instant feedback, no cloud round-trips while you're iterating.
| Deploy Target | Prod Database | Dev Adapter | Dev Target | Notes |
|---|---|---|---|---|
| Cloudflare Workers | d1 | sqlite | node | Same SQL dialect |
| Vercel Edge | neon | pglite | node | No server needed |
| Vercel Edge | turso | sqlite | node | Same SQL dialect |
| Deno Deploy | neon | pg | deno | Requires local PostgreSQL |
The browser target enables something different: offline-first applications. I'm building an offline scoring app for my dance showcase using the same models and views as the main Rails app. When hotel wifi inevitably fails mid-event, scoring continues. The browser app syncs when connectivity returns, treating the Rails server as an API.
The Vision, Revisited
The goal isn't to replace Rails. It's to give Rails applications more places to run—especially places Rails can't reach on its own.
Write a standard Rails app. Run it on your laptop with SQLite. Deploy it to Fly.io or Render with Postgres. Or transpile it and deploy to Cloudflare Workers with D1—global distribution, scales on demand. Or run it entirely in the browser with IndexedDB—offline-first, zero infrastructure, data stays on the user's device.
V8 Isolates and browsers are the sweet spots: platforms where transpilation isn't just an option, it's the only way to get there. One codebase. Many runtimes. That's the vision.
Ruby2JS is open source: github.com/ruby2js/ruby2js