# Deployments

`keel deploy` builds and releases your application. It uses CodeBuild by default; pass `--local` to build with Docker on your machine. A DynamoDB lock serializes deployments to each app and environment, and Keel records every deployment in its history.

```bash
# Deploy all services via CodeBuild
keel deploy

# Deploy a single service and stream progress
keel deploy web --watch

# Build locally instead of using CodeBuild
keel deploy web --local

# Override a stale deploy lock
keel deploy --force --lock-timeout 30

# Review recent deployments
keel history
keel history --limit 50
```

## The first deploy

`keel up` creates new services at zero tasks because their image tags do not exist until the first build. It then offers to run the initial deployment. Use `keel up --deploy -y` to do both without prompts. After the first deployment, `keel scale` and autoscaling control task counts; later infrastructure updates do not reset them to the value in the config.

## Docker or buildpacks {#buildpacks}

`pipeline.build` selects `auto` (the default), `docker`, or `buildpack`. In `auto` mode, Keel uses Docker when it finds a Dockerfile and Cloud Native Buildpacks with `heroku/builder:24` otherwise. `keel init`, `keel up`, and `keel deploy` print the selected strategy and reason.

Set the strategy explicitly when it should not depend on the working tree. For example, `build: docker` fails if the Dockerfile is missing instead of switching to buildpacks. For buildpack builds:

- A buildpack build produces one image for the app. Use `command` to select Procfile process types such as `["web"]` and `["worker"]`.
- Fargate requires the builder to produce an x86_64 image. Keel pins `pack_version` for reproducible builds and uses `BUILD_GENERAL1_MEDIUM` in CodeBuild; `keel cost` includes that size.

## The deploy pipeline

`keel deploy` separates building, release work, and promotion:

```
build  →  register a revision per service  →  run release once  →  promote each
```

1. **Preflight** — Keel checks the repository and verifies that CodeBuild can reach the selected commit.
2. **Build** — Services that share a Dockerfile also share a build: Keel builds the image once and tags it for each service. A service with its own `dockerfile` gets a separate build.
3. **Register** — a new task definition revision per service, pointing at the new image.
4. **Release** — the [`release:` command](./release-and-run) runs once, on the new revision, before any service takes traffic. A non-zero exit fails the deploy and leaves every service on its previous revision.
5. **Promote** — each service is updated to the new revision. A service still at zero tasks (a fresh environment) is started in the same call that pins the revision.

With no target, `keel deploy` deploys all services and [static sites](./static-sites). It publishes sites after services, inside the same deploy lock, so a frontend is not released before its API. Sites built in CodeBuild use the same commit as the services. You can also name one service or site. Scheduled services use the new revision on their next run and are not watched during deployment.

You can also deploy from the dashboard: `D` on the Deploys tab (or Deploy in a service's action menu) shows a plan card — commit, branch, services, release command, and every preflight warning individually — and enter confirms; progress streams into the tab. See the [Dashboard](./dashboard).

## Where the build gets your code

CodeBuild clones the commit you are deploying, so that commit has to be on a remote it can reach. `keel deploy` checks this before it builds anything, and what it does about a missing commit depends on who owns the repository.

### `source: github`

`pipeline.repo` names the repository CodeBuild clones. Keel does not push to GitHub repositories. If the selected commit is not on the remote, deployment stops and prints the `git push` command to run.

A private repository needs a CodeConnections connection so CodeBuild can clone it. Create one per GitHub organization:

```bash
keel auth connect github my-org
```

The connection is created `PENDING`; finishing it means authorizing the AWS Connector GitHub App against the organization in the AWS console, which has no API. The command prints the URL and waits.

The console's **App Installation** field must name the GitHub App installation. Although AWS labels it optional, leaving it blank can produce a connection that reaches `AVAILABLE` while every CodeBuild project or clone fails. Follow the full [Connecting GitHub](../github-connections) procedure and troubleshooting checklist.

If a project is created but `DOWNLOAD_SOURCE` fails with “Failed to get access token,” run `keel up` with the current build before retrying the deploy. The CodeBuild role needs `GetConnectionToken` and `GetConnection` as well as `UseConnection`; Keel grants both the older `codestar-connections` and current `codeconnections` ARN spellings because existing connections retain the name they were created under. This is an infrastructure policy change, so `keel deploy` alone cannot apply it.

One connection covers an **organization**, not a repository or an app. The ARN is stored at `/keel/connections/github/<org>` in SSM, so every app deploying from that organization finds it with no further configuration. Set `pipeline.connection_arn` to override it for one app. `keel deploy` checks the connection is authorized before building — a connection reverts to `PENDING` if the app is uninstalled or access revoked, and that would otherwise surface as a clone failure minutes into a build.

### `source: codecommit` with `repo` set

Keel clones an existing CodeCommit repository but does not push to or manage it.

### `source: codecommit` with `repo` unset

Keel creates a `<app>-<env>` repository as a build input and adds it as the `keel` git remote. Your team can continue using its existing repository. Before building, `keel deploy` offers to push the selected commit to the Keel remote:

```
$ keel deploy
Deploying acme-api/production
  commit  abc1234  fix session timeout
  branch  main
  remote  keel (git-codecommit.us-east-1.amazonaws.com/v1/repos/acme-api-production)

  ! commit abc1234 is not on "keel", so CodeBuild cannot clone it
    Push abc1234 to the acme-api-production source repository and continue? [Y/n]
```

Use `--no-push` to turn the offer off, and `--yes` to accept it unattended.

## Rolling back

`keel rollback` points a service back at the task-definition revision it ran before. Nothing is rebuilt — the revision already exists and its image is already in ECR — so it completes in seconds:

```bash
keel rollback              # every service, to its previous revision
keel rollback web --to 41  # one service, to a specific revision
```

**The release command is **not** re-run: rolling the code back does not roll a migration back.**

Each successful deploy records the exact task-definition revision it registered. On an operator session, rollback plans from that history without needing a broad ECS read, then the control plane brokers an update restricted to the selected service and an existing revision.

## Unapplied configuration

An apply records the effective merged `keel.yml`. Other commands compare the current file with that record and print a notice when fields have changed but have not been applied; the dashboard shows the same warning above every tab.

This is especially important for changes a deploy does not apply. `keel deploy` changes the image while CPU, memory, command, networking, database size, audit recording, and other infrastructure settings remain whatever the last successful `keel up` or `keel infra apply` provisioned. Run `keel up` to converge them.

No record, an unreadable record, or a dropped snapshot is treated as unknown rather than as drift. Values resolved outside `keel.yml`, such as a GitHub connection ARN, are excluded so a warning does not fire after every command. Set `KEEL_NO_DRIFT_CHECK=1` to suppress the CLI notice.

## Reconciling interrupted deploys

If a deploy is interrupted (network drop, ctrl-C, machine sleep), its record can be left in a non-terminal state. `keel deploy reconcile` inspects real AWS state and lets you resume, mark, or skip each stale record:

```bash
keel deploy reconcile
```
