DEV Community

pickuma
pickuma

Posted on • Originally published at pickuma.com

Kamal 2 review: deploying containers without Kubernetes in 2026

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.

Kamal 2 added first-class support for running multiple apps on a single server. kamal-proxy routes by host, so one modest VPS can serve app-a.com and app-b.com from separate containers with separate certs. For a portfolio of side projects, that consolidation alone can be the reason to switch.

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.

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.

Treat databases and anything with persistent state with extra care. Kamal swaps app containers freely, but a deploy that touches a volume-backed database container can lose data if you haven't pinned volumes and backups yourself. Run stateful services as managed offerings, or on hosts you deploy to deliberately, not as part of the routine app deploy cycle.

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.


Originally published at pickuma.com. Subscribe to the RSS or follow @pickuma.bsky.social for new reviews.

Top comments (0)