pickuma.
Infrastructure

Kamal 2 review: deploying containers without Kubernetes in 2026

A measured look at Kamal 2 — 37signals' Docker-over-SSH deploy tool. What kamal-proxy changed, where it fits, and where it hands the hard problems back to you.

6 min read

Kamal is the deployment tool 37signals built to move Basecamp, HEY, and their ONCE products off managed cloud platforms and onto rented bare metal. Version 2 landed in October 2024, and the headline change is that it no longer leans on Traefik for routing. You point it at one or more servers with SSH access, hand it a Docker image, and it runs the container, terminates TLS, and swaps versions with zero downtime. No control plane, no YAML manifests for pods and services, no cluster to babysit.

We ran Kamal 2 against a small Rails app and a plain Node service to see how much of the “deploy like it’s 2010, scale like it’s 2026” pitch holds up. Here’s where it earns its place and where it quietly hands the hard problems back to you.

What Kamal 2 actually does

At its core, Kamal is a thin orchestration layer over Docker and SSH. Your config/deploy.yml names the image, the servers, the registry, and the environment. Run kamal setup once and it installs Docker on each host, logs into your registry, boots the proxy, and starts your app. After that, kamal deploy builds or pulls the image, pushes it to every server, health-checks the new container, and cuts traffic over only when that check passes.

The piece that makes this feel current is kamal-proxy, a small Go reverse proxy 37signals wrote to replace Traefik in v2. It handles request routing, automatic Let’s Encrypt certificates, and the connection draining that makes deploys zero-downtime. In v1, getting Traefik labels right was the most common source of “why is my deploy stuck” threads. Kamal 2 folds that into a first-party component you configure through the same deploy.yml, which is a genuine reduction in moving parts.

Secrets got simpler too. Kamal 2 reads them from .kamal/secrets, which can pull from your shell environment, 1Password, or any command that prints a value. You reference them by name in the config and Kamal injects them as environment variables at boot. There’s no separate secrets store to provision and no plaintext credentials sitting in the repo.

The kamal-proxy shift and what changed from v1

If you used MRSK or Kamal 1, the upgrade is not a no-op. The proxy change means your routing config moves out of Traefik labels and into a proxy: block. Healthcheck behavior changed as well: kamal-proxy waits for your app to report healthy on a configurable path before sending traffic, so a missing or slow /up endpoint will stall a deploy in a way the old setup didn’t.

The payoff is fewer surprises in steady state. A v2 deploy is a sequence you can read top to bottom: build, push, boot, health-check, drain old, route new. When something breaks, the failure is usually in one of those steps rather than buried in proxy label resolution. kamal rollback restores the previous container in seconds because the old image is still sitting on the host.

What you give up is anything resembling a scheduler. Kamal does not reschedule containers when a host dies, does not autoscale, and does not spread replicas across a fleet for you. If a server goes down, that server’s traffic goes down with it until you fix the box or pull it from the config. Kamal’s position is that most apps don’t need a scheduler, and for a large share of them that’s honestly correct — but you should decide that on purpose, not discover it during an outage.

Cursor

Kamal config is hand-written YAML, and an AI editor that can read your Dockerfile and deploy.yml together cuts the trial-and-error on proxy and healthcheck settings.

Free tier; Pro $20/mo

Try Cursor

Affiliate link · We earn a commission at no cost to you.

Where Kamal 2 fits (and where it doesn’t)

Kamal 2 is a strong default when you control a handful of long-lived servers and want deploys you fully understand. Solo developers, small teams running a monolith, and anyone moving off a triple-digit monthly PaaS bill to a couple of VPS boxes are the obvious fit. The mental model is small enough to hold in your head, and the cost story is compelling: a single server can host the app, a database container, and background workers.

It fits less well when you need elastic capacity, multi-region failover, or per-request autoscaling. Stateful services are the sharpest edge. Kamal will happily run a Postgres container, but it does nothing to manage backups, replication, or failover — that part is entirely yours.

The honest summary: Kamal 2 doesn’t compete with Kubernetes on what Kubernetes does. It competes on whether you needed Kubernetes at all. For a large slice of apps that were over-provisioned onto a cluster for resume reasons, the answer is no — and Kamal 2 is the most polished version of that argument shipped so far.

FAQ

Is Kamal 2 only for Ruby on Rails apps?+
No. Kamal is distributed as a Ruby gem, but it deploys any Docker image. It's used heavily in the Rails community because 37signals built it, yet a Node, Go, or Python service in a container deploys the same way.
Do I still need Docker knowledge to use Kamal 2?+
Yes. Kamal orchestrates Docker, it doesn't hide it. You write a Dockerfile, and you'll debug at the container level when something fails. Kamal removes cluster complexity, not container fundamentals.
How is Kamal 2 different from Kamal 1?+
The biggest change is replacing Traefik with the first-party kamal-proxy for routing, TLS, and zero-downtime cutover. v2 also simplified secrets handling and added running multiple apps on one server. Upgrading requires migrating your routing config.

Related tools

Some links above are affiliate links. We may earn a commission if you sign up. See our disclosure for details.

Related reading

See all Infrastructure articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.