What does Kamal need on a target server before it can deploy your app there, and how does the container image actually get onto that host?
answer
- no agent on the box
- your SSH login is the API
- registry in the middle, not scp
- docker CLI driven over SSH
- config/deploy.yml plus a secrets file
basics
~20 sKamal needs only an SSH login that can run Docker on the host — no agent and no control plane. It builds your image, pushes it to a container registry, then drives docker commands over SSH so each server pulls and runs it.
solid answer
~40 sKamal is a CLI you run from your machine or from CI; the servers run nothing of Kamal's. The prerequisites are small: a Linux host reachable over SSH with a user that can talk to the Docker daemon, a container registry both the build machine and the servers can reach plus credentials for it, and a `config/deploy.yml` in the repo listing the `service`, `image`, `servers` and `registry`. The image does not travel over SSH — Kamal builds it with buildx, tags it with the git revision, pushes it to the registry, and then each host pulls that tag. `kamal setup` does the first-run work, including bootstrapping Docker on a host that does not have it. Everything after that is Kamal opening SSH sessions and running `docker` commands on your behalf.
code
yaml · 25 linesservice: myapp
image: acme/myapp
servers:
web:
- 192.168.0.1
- 192.168.0.2
job:
hosts:
- 192.168.0.3
cmd: bin/jobs
registry:
username: acme
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
env:
clear:
RAILS_ENV: production
secret:
- RAILS_MASTER_KEYgo deeper
Be able to say the shape out loud: SSH plus Docker on the host, image pushed to a registry, servers pull it, one YAML file in the repo describes all of it.
Explain the moving parts — roles versus accessories, where secrets live versus plain env vars, and why builder: arch: exists when your laptop and your servers differ in CPU architecture.
Talk about operating it: who holds the SSH key, why deploys should run from CI rather than laptops, and how registry credentials reach the hosts without ending up in the repo.
Own the access model. Argue about the blast radius of a deploy credential that is a production SSH login, and what compensating controls (bastion, short-lived certificates, CI-only deploys) you would require before adopting this.
## What Kamal is Kamal (from 37signals; it was called MRSK before version 1.0) deploys containers to plain servers you already own. It is a command-line tool, not a service: there is no scheduler, no cluster membership, and nothing of Kamal's running permanently on the hosts except the containers it starts. You run `kamal deploy` from a laptop or, more usually, from a CI job, and it does over SSH exactly what you would have done by hand with `docker` commands — just consistently, in the right order, on every host at once. ## The three things a host needs 1. **An SSH login that can use Docker.** Kamal connects as the user named under `ssh: user:` (root by default) and runs `docker` as that user. If Docker is missing, Kamal 2's `kamal server bootstrap` — invoked as part of `kamal setup` — installs it. That is the entire server-side prerequisite list. 2. **A reachable container registry, with credentials.** Docker Hub, GHCR, ECR, or a private one. The `registry:` block names the username and the environment variable holding the password, and Kamal runs `docker login` on each server so it can pull. 3. **Config in the repo.** `config/deploy.yml` holds the service name, image name, the `servers:` list grouped by role, the registry, and environment variables. Secrets live outside it — in `.kamal/secrets` in Kamal 2.x, or in a `.env` file pushed with `kamal env push` in 1.x. ## How the image travels: through the registry, not the SSH pipe This is the part candidates most often get wrong. Kamal builds the image locally (or on a remote builder host, via `builder: remote:`), tags it with the git revision, and **pushes it to the registry**. Each server then **pulls** that tag. The registry is the fan-out point, which is why the servers need credentials of their own and why `builder: arch:` matters — building on an Apple Silicon laptop for amd64 servers produces an image the hosts cannot run unless you say so: ```yaml builder: arch: amd64 ``` ## What actually runs during a deploy `kamal deploy` opens SSH sessions to every host listed under `servers:` and executes docker commands there. Servers are grouped by **role**: the `web` role is the one the proxy fronts; other roles such as `job` run the same image with a different `cmd:`. Longer-lived dependencies — Postgres, Redis — are declared as `accessories:` and booted separately with `kamal accessory boot`, deliberately outside the app's release cycle. Kamal takes a deploy lock first so two deploys cannot interleave, and it records what it ran, which you can read back with `kamal audit`. Day-to-day inspection uses the same shape of command: `kamal app logs -f`, `kamal app exec`, `kamal details`. ## What is deliberately absent No agent process, no control plane, no cluster API, no scheduler, no CRDs. The API *is* your SSH login. That has one important consequence worth saying out loud in an interview: whoever can deploy holds SSH credentials to production. That is why teams normally run Kamal from a CI job with a dedicated deploy key rather than from individual laptops, and why the blast radius of that key is the thing to think hardest about. It is also the honest tradeoff of the whole tool — you trade a control plane you have to operate for a credential you have to protect.
- If the servers pull from a registry, what breaks when your build machine is an Apple Silicon laptop?You produce an arm64 image and the amd64 servers cannot run it — the container fails immediately after the pull. Set `builder: arch: amd64` so buildx cross-builds for the target, or use `builder: remote:` to build on a host with the right architecture. Building in CI on an amd64 runner sidesteps the problem entirely.
- Why are databases declared as accessories rather than as another server role?Roles run your application image and are replaced on every deploy; an accessory is a separate long-lived container with its own image and volumes, booted once with `kamal accessory boot` and left alone. You do not want Postgres torn down and recreated every time you ship application code, and accessories are excluded from the rolling release cycle for exactly that reason.
saying these in an interview costs you the question
- Thinks Kamal installs a permanent agent or daemon on each server
- Believes Kamal copies the image over SSH instead of via a registry
- Assumes Kamal needs Kubernetes or Docker Swarm underneath it
- Forgets the servers themselves need registry credentials to pull
- Claims Kamal only works for Rails applications