Docs · Deployment

Deploying with Kamal

One-command Docker deploys with no PaaS, and the deploy topology.

One Shot deploys with Kamal: one command, any Docker host you control, no proprietary platform-as-a-service subscription in the loop. This site's own production deployment, shiponeshot.com, runs this exact setup on a small server it shares with another unrelated app, each fully independent with its own service name, image, volume, and port.

Why Kamal, and what that trades away

Kamal builds a Docker image, ships it to any server with Docker installed, and runs it there, handling zero-downtime restarts and basic proxying itself. Compared to a managed PaaS, you give up one-click scaling and a managed database in exchange for running on whatever server you already have (or a $5 to $20/month VPS), with no per-seat or per-request platform fee layered on top of your infrastructure cost. This fits a kit built around SQLite and Solid Queue: the whole app, including its background jobs and its database, lives inside one container, on one machine, with no separate services to provision.

First deploy

bin/kamal setup

This builds the image and boots the app for the first time. Verify it worked:

bin/kamal logs -f

Then confirm the app answers on its own, and through whatever's in front of it (see Edge and DNS Setup):

curl -sI http://127.0.0.1:<your-port>/up   # on the server itself
curl -sI https://yourdomain.com/up          # through your public edge

Every deploy after the first

bin/kamal deploy

A pre-app-boot hook removes the previous container before starting the new one, since the host port is fixed; expect a few seconds of downtime on each deploy, not a rolling zero-downtime handoff. For an app with Solid Queue running in-process, this also means jobs pause briefly during that window rather than continuing on a separate worker process.

Useful commands

bin/kamal logs -f                              # tail production logs
bin/kamal console                              # a Rails console on the server
bin/kamal app exec "bin/rails db:migrate"      # run a one-off command

CI/CD

A GitHub Actions workflow can mirror bin/check exactly, running the same bin/* wrappers (Brakeman, bundler-audit, gitleaks, importmap audit, RuboCop, RSpec) as separate parallel jobs, so a green bin/check locally predicts a green pipeline. Because Kamal deploys need direct access to your server, the deploy job itself typically runs on a self-hosted runner with network access to that server, rather than GitHub's own hosted runners, and triggers kamal deploy automatically on a green main.

Next

Before your first deploy, work through the checklist: First Deploy Checklist.