View as Markdown

Migrate from Kodiak to Mergify

Map your .kodiak.toml automerge, blocking, and commit message settings to Mergify's Merge Queue.


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 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.

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 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 to 1 and keep the default batch_size of 1. Mergify then runs in-place checks: 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, the setting Kodiak relies on to update branches, so you can switch without touching your branch protection.

  1. Install the Mergify GitHub App 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.

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:

.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:

.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 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)$.

  • merge.automerge_label → auto_merge_conditions 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:

    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 condition reads the update type from Dependabot’s commit message. Kodiak’s versions = ["minor", "patch"] becomes:

    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 ~=.

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, 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 on it. To update a branch without merging it, comment @mergifyio update.

Merge Method and Commit Message

Section titled Merge Method and Commit Message
  • merge.method → merge_method 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. A pull request goes to the first queue rule whose queue_conditions it matches, so list it first:

    queue_rules:
      - name: rebase
        merge_method: rebase
        queue_conditions:
          - label = rebase
      - name: default
        merge_method: squash
    
  • merge.message.* → commit_message_format on the queue rule:

    KodiakMergify
    title = "github_default"title: inherit (the default)
    title = "pull_request_title", include_pr_number = truetitle: pr-title: {title} (#{number})
    body = "github_default"body: inherit (the default)
    body = "pull_request_body"body: pr-body
    body = "empty"body: empty
    include_coauthors = truetrailers: [co-authored-by]
    strip_html_comments = truenothing to set: pr-body always leaves out HTML comments

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

  • 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 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 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, 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.

  • First come, first served → Mergify’s default: a pull request joins the end of the queue.

  • merge.priority_merge_label → a priority rule 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 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, 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.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 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:

    queue_rules:
      - name: default
        checks_timeout: 30 min
    
  • 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 repository setting. Kodiak stands aside when that setting is on, so you can enable it before you switch.

  • 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.

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: test several pull requests in one CI run, and let Mergify split a failing batch to find the culprit.

  • 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 turned off, or Mergify added as a bypass actor. Mergify still tests every pull request against the latest base branch.

  • Scopes: test and merge pull requests that touch unrelated parts of a monorepo independently.

  • Two-step CI: run a fast suite to enter the queue and the full suite only before the merge.

  • 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: see how long pull requests wait and where that time goes.

Was this page helpful?