# Release Commands & One-off Tasks

## The `release:` command

A `release:` command runs once per deployment on the new task definition, before services receive traffic. A non-zero exit stops the deployment and leaves services on their previous revisions.

```yaml
services:
  web:
    type: web
    port: 3000
    health_check: /up
    command: bundle exec thrust ./bin/rails server
  sidekiq:
    type: worker
    command: ["bundle", "exec", "sidekiq"]

release:
  command: bundle exec rails db:migrate
  service: web          # whose task role, security group, and env to run with
  timeout: 20m          # default 15m
```

`release:` also accepts the shorthand forms:

```yaml
release: bundle exec rails db:migrate
release: ["sh", "-c", "a && b"]
```

Use `--skip-release` to skip the command once or `--release-timeout` to override its timeout. The release runs inside the deploy lock, so its timeout must be shorter than `--lock-timeout`.

## `keel run` — one-off tasks

```bash
keel run -- bundle exec rails db:migrate
keel run --service sidekiq -- rake reports:backfill
keel run -i -- bundle exec rails console
```

`keel run` starts a new ECS task with the selected service's task definition, IAM role, security group, and environment. Use it for one-off work that does not require an existing healthy container. `keel exec`, by contrast, attaches to a running container.

`-i` attaches a terminal for a console or REPL. It requires `/bin/sh` in the image plus the AWS CLI and Session Manager plugin on your PATH. Keel stops the task when the session ends.

The command runs with the chosen service's role and security group. If only one of your services can reach the database, name it with `--service` or `release.service`.

## `keel exec` — shell into a running task

```bash
keel exec web
keel exec web -c 'bin/rails runner "puts User.count"'
```

Requires `/bin/sh` in the image. Keel speaks the SSM session protocol itself, so it needs neither the AWS CLI nor `session-manager-plugin`; `--legacy-session` uses those external tools as an escape hatch.

When `audit.exec.require_reason` is enabled, pass `--reason`. In the dashboard the exec shell opens in a pane instead of replacing the interface; `ctrl+c` is sent to the shell and `ctrl+]` exits the pane.
