## The command

```bash
bin/kamal app containers          # find the previous version tag
bin/kamal rollback <version>
curl -sI https://yourdomain.com/up | head -1
```

`kamal app containers` lists what is on the host, including stopped containers from previous
releases. The version is the git SHA the image was built from, and the image name it matches comes
from `config/deploy.yml`.

## What rolls back and what does not

Your application code rolls back completely. The container is the unit, so gems, assets and
configuration all return to the previous state together.

Your data does not. The SQLite file lives on a mounted volume precisely so that it survives
container swaps, which is the behavior you want every day except this one. If the bad deploy ran a
migration that dropped a column or rewrote rows, rolling the code back leaves the old code looking
at a schema it does not expect.

## Write migrations that can be rolled back

This is the real answer, and it is a habit rather than a command. Split anything destructive into
two deploys:

1. **Deploy one:** add the new column, write to both, read from the old one. Nothing is removed.
2. **Deploy two:** read from the new column, then remove the old one, once deploy one has been live
   long enough that you would not roll back past it.

Between those two deploys, `kamal rollback` is safe, because the schema supports both versions of
the code. This is the only reliable way to keep a rollback honest, and it costs one extra deploy.

## Check what the migration actually did

```bash
bin/kamal app exec 'bin/rails db:migrate:status' | tail -20
```

That lists which migrations have run on the server, which is the thing to look at before deciding
whether a rollback is enough or whether you need to write a corrective migration instead. Compare it
against `db/schema.rb` in the commit you are rolling back to: if the two disagree about a column,
the rollback needs a migration in front of it.
