## What Kamal actually needs

Three things, and nothing else: a server you can SSH into as root or a sudoer, somewhere to push a
container image (Docker Hub, GitHub Container Registry, anything), and Docker installed on that
server. Kamal installs Docker for you on first run if it is missing.

The whole deployment target is one file, `config/deploy.yml`:

```bash
bin/kamal setup     # first time only
bin/kamal deploy    # every time after that
```

`setup` is the one that does the heavy lifting. `deploy` builds the image, pushes it to the
registry, pulls it on the server, boots the new container, waits for `/up` to answer, and only then
moves traffic. If the health check never passes, the old container keeps serving and the deploy
fails loudly instead of taking the site down.

## Secrets do not live in the config file

`config/deploy.yml` is committed. Secrets are read from `.kamal/secrets`, which is not. In One Shot
the only one that must exist before production will boot is `RAILS_MASTER_KEY`:

```bash
kamal secrets print   # names only, no values
```

Everything else the app reads is optional and falls back to a local fake. See
[Bring Your Own Keys](/docs/bring-your-own-keys/optional-integrations) for the full list.

## What you give up

Kamal is not a PaaS, and the gap is real. There is no managed database, no automatic TLS unless you
let Kamal's proxy handle it, no dashboard, and no one to page at 3am. You own the box. For a single
app on SQLite, which is what One Shot ships, that trade is usually worth it: the app and its
database live on one disk, backups are a file copy, and the whole bill is the VPS.

It stops being worth it when you need more than one machine writing to the database. See
[Production Database and Storage](/docs/deployment/production-database-and-storage) for where that
line sits.

## Check it worked

```bash
curl -sI https://yourdomain.com/up | head -1
bin/kamal app logs --lines 50
```

A 200 from `/up` means Rails booted and the proxy is routing. If the deploy hangs on the health
check instead, the container is starting and dying; `kamal app logs` shows why, and the answer is
almost always a missing `RAILS_MASTER_KEY`.
