---
title: Migrate from Kodiak to Mergify
description: Map your .kodiak.toml automerge, blocking, and commit message settings to Mergify's Merge Queue.
---

[Kodiak](https://github.com/chdsbd/kodiak) merges pull requests that carry an
automerge label once they pass your GitHub branch protection. When that
protection requires up-to-date branches, Kodiak updates each branch with its
base just before merging it. Mergify's [Merge
Queue](/merge-queue) does the same job, and adds batching, parallel checks,
priorities, and scopes on top.

This page maps each `.kodiak.toml` option to its Mergify equivalent, and calls
out the few that have none.

:::tip[Need help?]

  [Reach out](mailto:support@mergify.com) with your `.kodiak.toml` and we'll
  propose an equivalent Mergify setup.

:::

## How the Two Queues Compare

Kodiak keeps one queue per target branch and works through it one pull request
at a time: it updates the branch at the front of the queue if it is behind,
waits for the required checks, merges it, then moves to the next one. A pull
request joins that queue once it has its required approvals and no failing
required check, and Kodiak only updates branches when *Require branches to be up
to date before merging* is enabled.

Mergify's queue also tests every pull request against the latest base branch
before merging it, and it reads your branch protection the same way: by
default, it [injects your branch protection and ruleset
requirements](/merge-queue/github-rulesets) into the queue's conditions, so you
do not have to restate your required checks and reviews. Unlike Kodiak,
Mergify does not have to test one pull request at a time.

To start with the behavior you have today, set
[`merge_queue.max_parallel_checks`](/merge-queue/performance#parallel-checks)
to `1` and keep the default [`batch_size`](/merge-queue/batches) of 1. Mergify
then runs [in-place checks](/merge-queue/batches#in-place-checks-no-batch-prs):
it updates the pull request at the front of the queue with its base branch,
waits for CI, and merges it, like Kodiak. This mode also works
with [*Require branches to be up to date before
merging*](/merge-queue/github-rulesets#require-branches-to-be-up-to-date),
the setting Kodiak relies on to update branches, so you can switch without
touching your branch protection.

## Typical Migration Plan

1. Install the [Mergify GitHub App](https://github.com/apps/mergify) on the
   repository and keep your branch protection as it is.

2. Add a `.mergify.yml` that reproduces your `.kodiak.toml`, using the example
   below and the option mapping that follows it.

3. Delete `.kodiak.toml` (or `.github/.kodiak.toml`) in the same pull request
   so the two bots never race to
   merge the same pull request. If your organization's `.github` repository
   also holds a `.kodiak.toml`, Kodiak falls back to it once the file is gone,
   so uninstall Kodiak from the repository instead of relying on the deletion.

4. Once the queue runs as expected, turn on [batches and parallel
   checks](#going-further).

## Side-by-Side Example

A `.kodiak.toml` that squash-merges labeled pull requests into `main` unless
they carry a blocking label, and lets a hotfix label jump the queue:

```toml title=.kodiak.toml
version = 1

[merge]
automerge_label = "ship it"
method = "squash"
blocking_labels = ["do not merge"]
priority_merge_label = "hotfix"

[merge.message]
title = "pull_request_title"
body = "pull_request_body"
strip_html_comments = true
include_coauthors = true
```

The equivalent Mergify configuration:

```yaml title=.mergify.yml
merge_queue:
  max_parallel_checks: 1

merge_protections_settings:
  auto_merge_conditions:
    - label = ship it

merge_protections:
  - name: not blocked
    if:
      - base = main
    success_conditions:
      - -label = do not merge
      - "-title ~= ^WIP:"

queue_rules:
  - name: default
    merge_method: squash
    commit_message_format:
      title: pr-title
      body: pr-body
      trailers:
        - co-authored-by

priority_rules:
  - name: hotfix
    conditions:
      - label = hotfix
    priority: high
```

The [merge protection](/merge-protections/custom-rules) holds the conditions
that Kodiak checks before it merges: Mergify merges a pull request only once
every active protection passes. Its `if` makes it apply to pull requests that
target `main`; if Kodiak merges into more than one branch, match them all with
a regex such as `base ~= ^(main|develop)$`.

:::note
  The `merge_protections` and `merge_protections_settings` sections above need
  [Merge Protections](/merge-protections/setup) enabled on the repository. With
  that product off, Mergify does not evaluate the `merge_protections` rules and
  `auto_merge_conditions` has no effect, so pull requests reach the queue
  through the [`@mergifyio queue`](/commands/queue) command.
:::

## Option Mapping

### Triggering a Merge

- **`merge.automerge_label`** → [`auto_merge_conditions`](/merge-protections/auto-merge)
  queues a pull request as soon as it carries the label, and removing the
  label dequeues it, as it does with Kodiak. When `automerge_label` lists
  several labels, any one of them triggers a merge, so put them under an
  [`or`](/configuration/conditions#combining-conditions-using-logical-operators):

  ```yaml
  merge_protections_settings:
    auto_merge_conditions:
      - or:
          - label = ship it
          - label = merge it
  ```

- **`merge.require_automerge_label = false`** → `auto_merge_conditions: true`,
  which queues every pull request. Your merge protections and branch protection
  still decide when it merges.

- **`merge.automerge_dependencies`** → for Dependabot, the
  [`dependabot-update-type`](/integrations/dependabot#filtering-dependabot-prs)
  condition reads the update type from Dependabot's commit message. Kodiak's
  `versions = ["minor", "patch"]` becomes:

  ```yaml
  merge_protections_settings:
    auto_merge_conditions:
      - or:
          - label = ship it
          - and:
              - author = dependabot[bot]
              - dependabot-update-type ~= ^version-update:semver-(minor|patch)$
              - -dependabot-update-type = version-update:semver-major
  ```

  A grouped update lists one update type per dependency and the `~=` line
  matches if any of them does, so the last line keeps out a group that
  contains a major bump, as Kodiak does by going by the largest bump. Like
  Kodiak, this skips a Dependabot pull request whose update type it cannot
  read, since no value matches. Kodiak names bots without their `[bot]` suffix
  (`dependabot`), while Mergify's `author` condition uses the full login
  (`dependabot[bot]`).

  Kodiak also reads the update type from other bots' pull requests, such as the
  version table in a Renovate description. Mergify reads Dependabot's metadata
  only, so match other bots on their title with `title ~=`.

### Blocking a Merge

Each of these becomes a line in the merge protection's `success_conditions`,
as in the example above.

- **`merge.blocking_labels`** → `-label = <name>`, one line per label.

- **`merge.blocking_title_regex`** → `-title ~= <regex>`. Kodiak blocks titles
  matching `^WIP:.*` even when the option is not set, so the example above adds
  `"-title ~= ^WIP:"` to keep that behavior. Quote the condition when it ends
  with a colon, or YAML reads it as a key.

- **`merge.block_on_reviews_requested`** → `"#review-requested = 0"`, if you
  used this deprecated option.

- **`merge.block_on_neutral_required_check_runs`** → `check-success = <name>`
  for that check. Unlike GitHub's branch protection, `check-success` [only
  matches a `success`
  conclusion](/configuration/conditions#which-check-run-a-condition-reads), so
  a neutral run blocks the merge.

- **`disable_bot_label`** (`kodiak:disabled`) → there is no single label that
  turns Mergify off on a pull request, but a `-label = <name>` line keeps
  Mergify from merging it.

Two Kodiak behaviors need nothing on the Mergify side:

- **Draft pull requests**: neither Kodiak nor Mergify merges a draft.

- **`merge.do_not_merge`**: leave out `auto_merge_conditions`, and Mergify
  merges a pull request only when somebody runs
  [`@mergifyio queue`](/commands/queue) on it. To update a branch without
  merging it, comment [`@mergifyio update`](/commands/update).

### Merge Method and Commit Message

- **`merge.method`** → [`merge_method`](/merge-queue/merge-strategies) on the
  queue rule: `merge`, `squash`, or `rebase`. Kodiak's `rebase_fast_forward`,
  which moves the base branch to the tested commits without rewriting them, is
  Mergify's `fast-forward`. Left unset, Mergify uses a merge method the
  repository allows, as Kodiak does.

- **`kodiak: merge.method = '<method>'` labels** → add a queue rule for that
  method and route labeled pull requests to it with
  [`queue_conditions`](/merge-queue/rules#queue-vs-merge-conditions). A pull
  request goes to the first queue rule whose `queue_conditions` it matches, so
  list it first:

  ```yaml
  queue_rules:
    - name: rebase
      merge_method: rebase
      queue_conditions:
        - label = rebase
    - name: default
      merge_method: squash
  ```

- **`merge.message.*`** → [`commit_message_format`](/workflow/actions/merge#customizing-the-commit-message)
  on the queue rule:

  | Kodiak | Mergify |
  | ------ | ------- |
  | `title = "github_default"` | `title: inherit` (the default) |
  | `title = "pull_request_title"`, `include_pr_number = true` | `title: pr-title`: `{title} (#{number})` |
  | `body = "github_default"` | `body: inherit` (the default) |
  | `body = "pull_request_body"` | `body: pr-body` |
  | `body = "empty"` | `body: empty` |
  | `include_coauthors = true` | `trailers: [co-authored-by]` |
  | `strip_html_comments = true` | nothing to set: `pr-body` always leaves out HTML comments |

  Like Kodiak's coauthor options, `trailers` needs `body` set to `pr-body` or
  `empty`.

### Branch Updates

- **Updating at merge time** → automatic. With in-place checks, the queue
  updates the pull request at its front by merging the base branch into it,
  which is what Kodiak does. Set
  [`update_method: rebase`](/merge-queue/merge-strategies#combining-merge-and-update-methods)
  on the queue rule to rebase it instead.

- **`update.always`, `update.require_automerge_label`,
  `update.autoupdate_label`, and `merge.update_branch_immediately`** → not
  needed. Kodiak uses them to keep pull requests up to date before their turn
  in its queue. With in-place checks, Mergify updates a pull request when the
  queue tests it, which is the update the merge depends on. To bring a branch up to date by hand,
  comment [`@mergifyio update`](/commands/update) on its pull request.

- **`update.ignored_usernames`** → Kodiak drops such a pull request from its
  queue when it needs an update, because it may not touch the branch. Mergify's
  in-place checks do update it; once you enable [parallel
  checks](#going-further), Mergify tests the pull request on a temporary branch
  and leaves its own branch alone.

- **`merge.optimistic_updates`** → not needed. Kodiak uses it to choose
  between updating a pull request that is behind and waiting for its running
  checks first. Mergify's queue updates the pull request when its turn comes
  and runs CI on the updated code, so there is nothing to choose.

### Queue Order

- **First come, first served** → Mergify's default: a pull request joins the
  end of the queue.

- **`merge.priority_merge_label`** → a [priority rule](/merge-queue/priority)
  with `priority: high` on that label. By default the pull request is placed
  after the ones whose checks are already running; set
  [`allow_checks_interruption: true`](/merge-queue/priority#managing-check-interruptions-based-on-priority)
  to test it first.

- **`merge.prioritize_ready_to_merge`** → no exact equivalent. Mergify keeps
  the queue order. Once the queue tests on [batch pull
  requests](/merge-queue/batches), a pull request
  at the front of the queue that already contains the tip of the base branch
  merges without running CI again. See [Direct Merge](/merge-queue/direct-merge).

- **`merge.dont_wait_on_status_checks`** → Kodiak waits briefly for these
  checks, then skips the pull request instead of blocking its queue. Mergify
  only waits for the checks your branch protection and conditions require, so
  do not require a check that may never finish. For a required check that
  can hang, [`checks_timeout`](/merge-queue/lifecycle#checks-timeout) removes
  the pull request from the queue after a while. By default, it derives that
  time from your recent CI runs; set a duration to choose it yourself:

  ```yaml
  queue_rules:
    - name: default
      checks_timeout: 30 min
  ```

### Conflicts and Cleanup

- **`merge.notify_on_conflict`** → Mergify removes a pull request that
  conflicts with its base branch from the queue and says why in a comment.
  Unlike Kodiak, it leaves the label in place.

- **`merge.delete_branch_on_merge`** → enable GitHub's [Automatically delete
  head
  branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-the-automatic-deletion-of-branches)
  repository setting. Kodiak stands aside when that setting is on, so you can
  enable it before you switch.

## Options With No Equivalent

- **`approve.auto_approve_usernames`** and **`approve.auto_approve_labels`**: Mergify's
  queue does not approve pull requests. They need the approvals your branch
  protection requires, whoever opened them.

- **`merge.message.include_pr_number = false`**: `pr-title` always appends the
  pull request number.

- **`merge.message.cut_body_before`, `cut_body_after`, and
  `cut_body_and_text`**: `pr-body` uses the whole description. Since it leaves
  out HTML comments, a pull request template can keep text out of the commit
  message by wrapping it in `<!-- -->`.

- **`merge.message.strip_html_comments = false`** and **`body_type`**:
  `pr-body` always uses the Markdown description without its HTML comments.

- **`merge.message.include_pull_request_url`**: there is no `PR-URL:`
  trailer. The available trailers are `co-authored-by`, `approved-by`, and
  `merged-by`.

- **`merge.message.include_pull_request_author`**: Kodiak's own
  documentation describes it as no longer necessary.

## Going Further

Kodiak tests one pull request at a time, so the time to merge a queue of
pull requests grows with its length. Once your queue runs on Mergify, you can
change that:

- [Batches](/merge-queue/batches): test several pull requests in one CI run,
  and let Mergify split a failing batch to find the culprit.

- [Parallel checks](/merge-queue/performance#parallel-checks): test the next
  pull requests in the queue while the first one is still running. Batches and
  parallel checks test on temporary batch pull requests, so they need [*Require
  branches to be up to date before
  merging*](/merge-queue/github-rulesets#require-branches-to-be-up-to-date)
  turned off, or Mergify added as a bypass actor. Mergify still tests every pull
  request against the latest base branch.

- [Scopes](/merge-queue/scopes): test and merge pull requests that touch
  unrelated parts of a monorepo independently.

- [Two-step CI](/merge-queue/two-step): run a fast suite to enter the queue and
  the full suite only before the merge.

- [Merge Protections](/merge-protections): beyond the rule above, freeze
  merges during a release, hold a pull request until another one merges with
  `Depends-On`, or schedule a merge with `Merge-After`.

- [Queue monitoring](/merge-queue/monitoring): see how long pull requests wait
  and where that time goes.
