Kamal Runs Everything Now (Almost)

4 min read
Programming

I've been deploying with Kamal since before it hit v1. Back then it was still called MRSK and the docs were sparse. I set it up anyway because the promise was too good: deploy Docker containers to bare metal servers with zero downtime, no Kubernetes, no managed platform eating my budget.

That bet paid off.

Today almost everything I run sits behind Kamal. My Rails apps, my side projects, this blog. I've stuck with it through every major version bump, and each one made the setup simpler.

The only holdouts? A couple of WordPress sites. They still run as plain Docker containers, but even those sit behind kamal-proxy for SSL and routing. I didn't want to manage separate nginx configs when kamal-proxy already handles it.

They're not Kamal-deployed. But they're Kamal-proxied. Close enough.

37signals Just Did the Same Thing (at Scale)

37signals completed migrating their entire fleet to Kamal. Every app they've built over the past two decades. Basecamp. HEY. Campfire. The whole heritage lineup.

They extracted seven apps from the cloud. The initial projection was $7 million in savings over five years. DHH later updated that to over $10 million once they factored in the S3 exit and storage hardware.

That number keeps coming up in his posts. And honestly, it tracks. I don't run seven apps at 37signals scale, but I've felt the difference myself. No more per-dyno pricing. No more surprise bills when traffic spikes.

Remember Traefik Labels?

If you ran multiple apps on the same server with MRSK v1, you know the pain. You had to tangle through custom Traefik labels just to get auto SSL working across apps. Routing rules, certificate resolvers, middleware chains. It worked, but it wasn't pretty.

Kamal 2.0 replaced all of that with kamal-proxy. Multi-app on a single server just works now. No label soup. No Traefik config files. That alone was worth the upgrade.

Upright: Global Monitoring for $110/Month

37signals also open-sourced Upright, a synthetic monitoring system they built to replace Pingdom.

It's a Rails engine. You add the gem to a new Rails app, run the install generator, and deploy it to cheap VPS nodes around the world with Kamal. That's the whole setup.

What does it actually check? Playwright probes that log into your app, walk through real user flows, and capture video when things break. HTTP health checks every 30 seconds from each monitoring site.

It also does SMTP probes for email infrastructure and traceroute probes for network diagnostics. Not just is the site up. Actual user-flow verification.

Their production setup: four DigitalOcean droplets (about $24/month each) and one Hetzner server. Total cost for monitoring Basecamp, HEY, and all their other services globally? About $110/month.

You could run a minimal two-site setup for under $20/month on Hetzner alone. That's monitoring that would cost thousands on a SaaS platform. For $110.

The Tools Are Real Now

When I first set up MRSK, it felt like an early adopter's gamble. The community was tiny. You'd read the source code because the docs didn't cover your case.

Not anymore.

Rails 8 ships with Kamal as the default deployment tool. Josef Strzibny wrote a proper handbook that covers everything the official docs don't. CI/CD plugins exist. Kamal 2.0 brought canary deployments, maintenance mode, and multi-app support on a single server.

And now they're open-sourcing the monitoring too? The whole story is filling in. Deploy, proxy, monitor. All open source, all on your own hardware.

Do you still need Kubernetes? For some workloads, sure. For your Rails app, your side project, your small SaaS? Probably not.

I still need to figure out a clean way to bring those WordPress sites fully into Kamal. Someday. For now, kamal-proxy does the job just fine.

And that might be the whole point. You don't have to migrate everything at once. You just keep moving things over, piece by piece, until one day you look up and realize: Kamal runs everything now.

Almost.

Share this post

Comments

Leave a Comment

0/1000