Getting Started with Kamal
You are an expert in deploying applications with Kamal. Your goal is to take a
user from nothing to a running first deploy: install the tool, generate the
config, fill in the values, and run the first kamal setup against their
servers.
Before You Start
Check what already exists before asking questions:
- If
config/deploy.ymlis present, read it first and work from the values the user already has instead of starting from scratch. - If
.kamal/secretsis present, read it to see which secrets are already wired up (do not echo secret values back to the user).
Then confirm the essentials you will need:
- Servers — the IP addresses or hostnames Kamal will deploy to.
- Registry — which Docker registry the image is pushed to, and the username/password for it.
- Image name — what the built image will be called.
- SSH access —
kamal setupconnects over SSH (root by default, authenticated by your SSH key). - Committed code — Kamal builds from a
git archiveofHEAD, not your working tree; commit every file the image needs before deploying (see the preflight note in Step 5).
Step 1: Install Kamal
If you have a Ruby environment available, install Kamal globally:
gem install kamal
If you do not have Ruby installed, you can run Kamal from a Docker container instead. On macOS:
alias kamal='docker run -it --rm -v "${PWD}:/workdir" -v "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/basecamp/kamal:latest'
The dockerized path has limitations (the secret adapters and host environment variables are not available in the container). For the Linux alias and the full list of limitations, see references/dockerized-install.md.
Step 2: Initialize the Project
Inside your app directory, run:
kamal init
This creates the files you need to deploy:
Created configuration file in config/deploy.yml
Created .kamal/secrets file
Created sample hooks in .kamal/hooks
| File | Purpose |
|---|---|
config/deploy.yml |
Main deploy configuration |
.kamal/secrets |
Secrets referenced by the config |
.kamal/hooks |
Sample lifecycle hooks |
No Dockerfile yet? Kamal builds the standard
Dockerfileat your project root. Rails generates one withrails new; most other frameworks (Node/Next.js, Django, Go) do not — write one beforekamal setup, or build without one via buildpacks (see the build skill). A good shape is multi-stage: a deps stage that installs dependencies, a build stage that compiles the app, and a slim runner stage that copies only the built output. Common footguns: native modules often ship prebuilt binaries, so try building without a C toolchain before adding one; the framework's build step usually doesn't need runtime secrets or database access — verify by building with them absent instead of threading them through as build args; and run migrations from an ENTRYPOINT script at container start, not at build time — volumes aren't mounted during the image build.
Step 3: Write config/deploy.yml
Edit the generated config/deploy.yml. A first deploy can be as simple as this:
service: hey
image: 37s/hey
servers:
- 192.168.0.1
- 192.168.0.2
registry:
username: registry-user-name
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
env:
secret:
- RAILS_MASTER_KEY
What each key does:
| Key | Description |
|---|---|
service |
Required. Used as the container name prefix. |
image |
The Docker image name. It is pushed to the configured registry. |
servers |
The servers to deploy to. A plain list is implicitly assigned to the web role. |
registry |
Docker registry configuration. The password references a secret (looked up by name), not a literal value. |
builder |
Builder options. The starter config sets arch: amd64. |
env |
Environment variables. Names under secret are provided through your secrets file. |
The default registry is Docker Hub; you can change it with registry/server.
For every available configuration key, see the config skill.
App ships its own stack definition? If the project has a production
docker-compose.yml, Helm chart, or upstream install docs, treat it as the contract: map every service it runs to a Kamal accessory, an external service, or an explicit user-approved omission. Never silently drop or swap a component because the host looks too small — surface the constraint and let the user choose before deploying. See "Mirroring an existing stack" in the accessories skill.
Persisting data across deploys
Every deploy replaces the app container — anything written to the container's own filesystem (a SQLite database, uploaded files, anything else meant to persist) is discarded with the old container. Mount a named volume for any path that must survive:
volumes:
- "myapp_data:/app/data"
Point your app's writable paths at the mounted directory (e.g.
DATABASE_URL=file:/app/data/production.db), and chown that path to the container's
runtime user in the Dockerfile — Docker copies ownership from the image into a newly
created named volume on first use. Then confirm the data actually survives a redeploy
(run kamal deploy again and check) before trusting it — a plain docker restart keeps
the same container and proves nothing.
Step 4: Fill In .kamal/secrets
The registry/password and any env/secret entries above are looked up by name
in your environment and in .kamal/secrets. Set the registry password in your
environment, then edit .kamal/secrets to read it (plus RAILS_MASTER_KEY for
a production Rails app):
# .kamal/secrets
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$(cat config/master.key)
Each line maps a secret name to a value: an environment variable
($KAMAL_REGISTRY_PASSWORD) or the output of a command ($(cat config/master.key)).
For reading secrets from 1Password, LastPass, Bitwarden, AWS, GCP, Doppler, and
other vaults, see the secrets skill.
Step 5: Bootstrap and Deploy with kamal setup
With the config and secrets in place, run your first deploy:
Preflight — commit first. Kamal builds from a
git archiveofHEAD, not your working tree. Files that are new or modified but uncommitted — generated or newly added files (config/*.rb, freshly created credentials, view overrides) — are excluded from the image and fail the build withCOPY <file>: not found. Commit everything the DockerfileCOPYs before runningkamal setup. To build the working tree instead, setcontext: .(see the build skill).
kamal setup
kamal setup runs everything required to deploy to a fresh host:
- Install Docker on all servers, if it has permission and it is not already installed.
- Boot all accessories.
- Deploy the app (the same work as
kamal deploy).
What kamal setup does step by step
Expanded, the first deploy:
- Connects to the servers over SSH (root by default, authenticated by your SSH key).
- Installs Docker on any server missing it (using get.docker.com); root access via SSH is needed for this.
- Logs into the registry both locally and remotely.
- Builds the image using the standard Dockerfile in the root of the application.
- Pushes the image to the registry.
- Pulls the image from the registry onto the servers.
- Ensures kamal-proxy is running and accepting traffic on ports 80 and 443.
- Starts a new container with the version of the app that matches the current Git version hash.
- Tells kamal-proxy to route traffic to the new container once it responds with
200 OKtoGET /up. - Stops the old container running the previous version of the app.
- Prunes unused images and stopped containers so servers don't fill up.
All servers are now serving the app on port 80. If you run a single server, you're ready to go. If you run multiple servers, put a load balancer in front of them.
If
kamal setupis interrupted (SSH drop, network reset) partway through: the deploy lock may still be held on the server — check withkamal lock status. If the image already built and pushed, resume without rebuilding:kamal lock release, boot any accessories that aren't running yet (kamal accessory boot all; a name-conflict error for one that already booted is harmless), thenkamal deploy --skip-pushto deploy the already-pushed image. If the failure happened during build or push, just release the lock and rerunkamal setup. For lock details, see the deploy skill.
Bootstrapping Docker separately
kamal setup installs Docker for you when it has permission. To set up Docker on
the hosts explicitly — for example before a deploy — run:
kamal server bootstrap
This checks whether Docker is installed and, if not, attempts to install it via get.docker.com.
After the First Deploy
For subsequent deploys, or if your servers already have Docker installed, you do
not need setup again — just run:
kamal deploy
kamal deploy builds and deploys the currently checked-out version of the app
to all servers. For the full release workflow, see the deploy skill.
Useful Commands
| Command | What it does |
|---|---|
kamal init |
Create config stub in config/deploy.yml and secrets stub in .kamal |
kamal setup |
Setup all accessories, push the env, and deploy app to servers |
kamal deploy |
Deploy app to servers |
kamal server bootstrap |
Set up Docker to run Kamal apps |
kamal docs [SECTION] |
Show Kamal configuration documentation |
kamal help [COMMAND] |
Describe available commands or one specific command |
kamal version |
Show Kamal version |
Most-used global options
| Option | Description |
|---|---|
-c, --config-file=CONFIG_FILE |
Path to config file (default config/deploy.yml) |
-d, --destination=DESTINATION |
Destination for the config file (staging → deploy.staging.yml) |
-v, --verbose |
Detailed logging |
-q, --quiet |
Minimal logging |
-p, --primary |
Run commands only on the primary host instead of all |
-h, --hosts=HOSTS |
Run on these hosts instead of all (comma-separated, supports * wildcards) |
-r, --roles=ROLES |
Run on these roles instead of all (comma-separated, supports * wildcards) |
-H, --skip-hooks |
Don't run hooks |
For the full command list, the kamal server subcommands, and every global
option, see references/cli-reference.md. You can
also run kamal --help or kamal help [command] at any time.
Related Skills
- config: For the full set of
config/deploy.ymlkeys and defaults. - deploy: For routine releases with
kamal deployafter the first setup. - secrets: For
.kamal/secretsand reading secrets from vault adapters. - registry: For configuring Docker Hub, ECR, GCP Artifact Registry, and other registries.