Skip to documentation
MMirageDocs

Documentation

Route jobs

Compare CI providers

Try an existing build or test job on another provider before changing where it normally runs.

Set up a repository with one pull request. Mirage routes the jobs you select through your machines, and each keeps the runner it uses today as its fallback. Afterwards, compare any of those jobs on GitHub, local infrastructure, Cloudflare, or Blacksmith, and promote a supported provider through a reviewed pull request.

Each experiment runs an extra copy of a completed job on the same commit. The original run and its required checks remain unchanged. Routing and providers explains how comparisons, defaults, and fallback fit together.

For recommendations that Mirage discovers and tests automatically, see Automatic CI improvements. That flow includes dependency caching and uses separate service-funded trials, with optional Claude Code or ChatGPT proposal generation.

Choose a job

Start with build or test jobs on a runner whose image matches Mirage's machines: ubuntu-latest, ubuntu-24.04, ubuntu-24.04-arm, macos-latest, or macos-26, or a Blacksmith label on Ubuntu 24.04 or the latest or macOS 26 image, such as blacksmith-4vcpu-ubuntu-2404. Jobs using cloudflare-github-actions-runner are also supported: cloudflare-ubuntu-latest, the cloudflare-lite, cloudflare-basic, and cloudflare-standard-1 through cloudflare-standard-4 presets, and valid custom sizes such as cloudflare-vcpu:2-memory_mib:6144-disk_mb:12000 all map to Linux x64. Mirage keeps the exact original Cloudflare label as the fallback, including its custom size; keep the standalone runner's GitHub App and pool configured to serve that fallback. Jobs pinned to another image, such as ubuntu-22.04 or macos-15, keep running where they are, because your machines would run a different OS. The repository must be private and selected for the Mirage GitHub App. Set up an available machine or Cloudflare account for the provider you want to try.

Job discovery checks whether Mirage can route the job and, separately, whether it can copy the job into an independent experiment workflow. Mirage does not route jobs that already call reusable workflows, deployment and environment jobs, jobs whose name is computed with an expression, or matrix jobs whose runners are computed rather than listed.

A matrix job whose children all use one runner is routed as a whole: every child runs on your machines and falls back to that runner on its own. A matrix that picks its runner with runs-on: ${{ matrix.os }} is routed child by child: each child runs on the Mirage machine type for its own runner, such as Linux for ubuntu-latest or macOS for macos-latest, and falls back to that same runner. Children on runners Mirage cannot serve, such as Windows, keep running where they do now. Matrix jobs, and jobs that read event context, define outputs, set job-level concurrency, or live in a workflow with a manual workflow_dispatch trigger, are routed without an experiment workflow, so they cannot be compared across providers. Discovery explains the reason per job.

This is an optional setup path for existing GitHub-hosted, Blacksmith, or standalone Cloudflare jobs. A job already using the attempt expression from Configure workflows is not eligible for this onboarding flow. Use a separate eligible job or restore its static GitHub-hosted label before onboarding it.

Set up the repository

You don't edit runs-on yourself: the dashboard walks you through the setup and opens the pull request for you.

  1. Open Repos and select the repository. A repository that needs setup opens Set up routing; for one that already runs jobs through Mirage, choose Set up routing on its page.
  2. Ready check. Mirage checks that the GitHub App can open pull requests in the repository and that a machine available to the account has each runner type the selected jobs need Ready. Routed jobs, other than matrices that pick their runner with matrix.<key>, also need a Linux x64 machine for the routing step that runs first. Each problem names its fix, such as Pair a machine, Open machine for a runner that isn't ready, or Review on GitHub for App permissions you haven't approved.
  3. Jobs. Mirage reads the default branch's workflows and lists each job with what it runs on now, where it will run on Mirage, and the runner it falls back to. Every eligible job starts selected; clear any you want to leave alone. Jobs Mirage won't route say why.
  4. Setup pull request. Once the ready check passes, choose Open setup PR. Mirage opens one pull request for the whole repository, and the page follows it: its checks running on your machines, the merge, and the routes applying.
  5. Review the pull request in GitHub, then merge it yourself; Mirage never merges it. The page shows the routes as applied, and the next runs use your machines.

For each selected job, the pull request changes runs-on to Mirage's routing expression, adds the .github/workflows/mirage.yml routing workflow, and adds a standalone experiment workflow. Each job falls back to the runner it used before (a GitHub-hosted label, or its Blacksmith or standalone Cloudflare label) when its runner fails or no machine takes it within the repository’s queue timeout. Later changes to where jobs run, such as retry policy or which machines serve them, need no further pull requests.

If Mirage cannot run a job while the setup pull request is still open, that check fails with an explanation instead of falling back: fallback starts working once the routing workflow is on your default branch. That is why the ready check waits for your machines before the pull request opens. Routed jobs run on your machines only; Cloudflare stays available for experiments and direct Cloudflare jobs.

Compare providers

  1. In Runs, open a completed job that has finished onboarding.
  2. Select Experiment, choose the available providers, and launch the comparison.
  3. Compare each provider’s result and available capacity-wait, startup, and execution timings. Follow the GitHub links for full logs.

Experiments use the source job’s commit, so a newer push does not change what is being compared. They consume runner capacity and any applicable provider usage. An unavailable provider or a failed experiment does not cancel the source run or other providers in the comparison.

Only compare jobs whose steps are safe to run again. An experiment executes your code again even though its result does not gate the source pipeline.

Change the default

When a supported provider succeeds, select Promote in the comparison. Mirage opens a pull request to change the job’s runner selection. Review and merge that PR, then check Settings for the applied default and fallback before relying on the new route.

A comparison alone does not change future jobs. Cloudflare is currently available only for experiments in this flow, so it has no promotion action. To use it directly, configure an explicit Cloudflare workflow.

Blacksmith

Blacksmith runs jobs through its own GitHub App, so Mirage uses your Blacksmith account rather than providing capacity itself. Install Blacksmith’s GitHub App on each repository you compare, and run at least one job on a blacksmith-* label. Mirage offers Blacksmith in comparisons once it has seen Blacksmith run a job for your account in the last 30 days.

A Blacksmith comparison uses its smallest runner for the job’s profile. If Blacksmith does not pick the job up within 10 minutes, usually because its app cannot see the repository, Mirage cancels that comparison, records Blacksmith as unavailable, and adds a failed Mirage (experiment) - Blacksmith did not start the comparison check to the commit. Its details explain what happened and how to retry the comparison. Billing shows Blacksmith as its own source, priced from Blacksmith’s published per-minute rates before its free monthly minutes.