How do I migrate from self-hosted GitHub Actions runners without rewriting workflows?

Move from self-hosted GitHub Actions runners to Depot by changing the runs-on label. Your workflow steps, actions, secrets, checks, and GitHub interface stay the same while Depot operates the runner fleet.

Last updated 2026-09-09

You can migrate from self-hosted GitHub Actions runners to GitHub Actions runners managed by Depot without rewriting your workflows. Connect your GitHub organization to Depot, map each self-hosted runner label to a Depot GitHub Actions runner type, and change the runs-on value.

For example, a job routed to an eight-core self-hosted Linux runner:

 jobs:
   test:
-    runs-on: [self-hosted, linux, x64, 8-core]
+    runs-on: depot-ubuntu-24.04-8

The steps below runs-on don't change. The workflow still uses the same Marketplace actions, secrets, environments, permissions, triggers, job names, and pull request checks. GitHub still schedules the workflow and displays its logs. Depot replaces the machines running each job.

Each Depot job gets a fresh, single-tenant virtual machine that starts in seconds and is destroyed after the job. Depot manages capacity, runner registration, updates, monitoring, and cleanup. Runners are available for Linux x64, Linux Arm, Windows, and macOS. Linux and Windows sizes range from 2 to 64 vCPUs.

Inventory what the self-hosted labels really mean

The YAML change is small. The work is discovering everything hidden behind the old label.

Search .github/workflows/ and any reusable workflows for runs-on. For each self-hosted label or runner group, record:

  • Operating system, architecture, CPU, memory, and disk requirements
  • Software installed in the machine image outside the workflow
  • Files, package caches, or Docker layers expected to survive between jobs
  • Internal APIs, databases, registries, artifact stores, and cloud services the job reaches
  • Static IP allowlists, VPN connections, VPC routes, proxies, and DNS assumptions
  • IAM roles, workload identity, certificates, and credentials attached to the runner
  • Docker, nested virtualization, GPU, device, or kernel requirements
  • Repository and workflow access rules configured on GitHub runner groups

Don't assume a green workflow is self-contained. Long-lived self-hosted runners often accumulate tools and state that were never declared in YAML. Run the job on a clean copy of the current runner image, or remove its workspace and caches, to expose those dependencies before the migration.

Map labels to Depot GitHub Actions runner types

Choose a Depot label with the same operating system and architecture, then right-size it from actual CPU, memory, and disk usage. The runner types page lists the current images and machine sizes.

Common mappings include:

Current requirementDepot label
Ubuntu 24.04, x64, 2 vCPUdepot-ubuntu-24.04
Ubuntu 24.04, x64, 8 vCPUdepot-ubuntu-24.04-8
Ubuntu 24.04, Arm64, 8 vCPUdepot-ubuntu-24.04-arm-8
Windows Server 2022, 8 vCPUdepot-windows-2022-8
Current supported macOS runnerdepot-macos-latest

Depot's standard images follow GitHub's runner images, so software that exists on a GitHub-hosted image is generally available under the equivalent Depot label. Check the installed software list before rollout. If the self-hosted image contains large dependencies, proprietary tools, or mandatory security software, Depot can provide a custom runner image on the Business plan.

Windows jobs that require Hyper-V, including Docker workloads that depend on it, aren't compatible with Depot's Windows runners. Treat hardware, hypervisor, and kernel dependencies as explicit blockers rather than discovering them halfway through rollout.

Connect Depot without changing job behavior

Follow the GitHub Actions runner quickstart to connect a Depot organization and install the Depot Managed Runners GitHub app. The repository must belong to a GitHub organization, and an organization owner must complete the connection.

The runners register in GitHub's default runner group. Confirm that the group can access every repository in the rollout. If the group restricts access to selected workflows, verify those workflow reference patterns as well. Public repositories require the default runner group to allow public repository access.

Keep the job name unchanged during migration. Branch protection rules usually key off the check name, not the machine that ran it. Changing only runs-on isolates the infrastructure change and preserves required checks.

Move one representative job first

Start with a job that's important enough to expose real dependencies but easy to rerun and roll back. A unit test or build job is usually better than the production deployment job.

Change only its runner label. Run it against the same commit on the old fleet and Depot, then compare:

  • Job result and generated artifacts
  • Queue time and total execution time
  • CPU and memory utilization, with step timing and logs for disk or network investigation
  • Cache restore and save behavior
  • Access to private services and registries
  • Any scripts that depend on absolute paths, users, permissions, or installed tools

Depot provides live and historical CPU and memory metrics. Step timing and logs help identify jobs blocked on disk or network I/O. Fix portability problems in the workflow itself when practical. That keeps the job reproducible instead of moving hidden machine state into a new image.

Roll out by job family or repository after the canary is stable. Keep the self-hosted fleet available until scheduled, release, deployment, and infrequently used workflows have all run successfully on Depot.

Preserve private network access

A self-hosted runner is often inside a private network by accident rather than by design. Moving its compute requires an explicit path to the same resources.

Depot GitHub Actions runners can access private resources through Tailscale, Cloudflare WARP, AWS VPC peering, dedicated infrastructure with static outbound IPs, or Depot Managed. Depot Managed places the compute and cache data plane in an isolated sub-account inside your AWS organization while Depot continues to operate it.

Choose the access model before the canary if the job reaches an internal registry, database, API, or cloud service. Test DNS, authentication, routing, and bandwidth independently. A TCP connection succeeding doesn't prove that the runner has the right identity or that a large artifact transfer will perform well.

If the old fleet enforced outbound restrictions, reproduce the policy with Depot egress filters. Filters can allow or deny hostnames, IP addresses, and CIDRs on Linux runners. Tailscale and Depot egress filters can't currently be used together, so choose the control that matches the security requirement.

Make cache and image dependencies explicit

Don't copy a self-hosted runner's persistent workspace into the new setup. Reusing a machine between jobs makes the workflow faster only until stale state makes it wrong.

Keep actions/cache and actions/setup-* cache configuration in the workflow. Depot accelerates those existing cache operations without requiring a different action. Cache entries remain outside the ephemeral runner and are scoped by repository.

Install small or frequently changing dependencies in the workflow. Put large, stable, or proprietary dependencies in a custom runner image. Pin required tool versions when the result needs to be reproducible. If a job builds Docker images, validate registry credentials and cache behavior separately from the runner migration rather than changing the runner and build system in the same canary.

Keep rollback to one label change

Leave the old runner labels and capacity online during the rollout. If a Depot canary exposes an undeclared dependency, revert runs-on to the old self-hosted label and rerun the job:

 jobs:
   test:
-    runs-on: depot-ubuntu-24.04-8
+    runs-on: [self-hosted, linux, x64, 8-core]

Avoid changing action versions, cache keys, permissions, and runner infrastructure in the same pull request. A narrow change gives you a clean comparison and a reliable rollback.

If a Depot job remains queued, check the label syntax, default runner group access, selected workflow restrictions, and GitHub app repository permissions. The troubleshooting guide covers those checks and common machine-size or disk issues.

Retire the self-hosted fleet safely

Remove the old fleet only after every workflow category has completed on Depot, including scheduled jobs, release workflows, deployment paths, reusable workflows, and Dependabot jobs where applicable.

Then:

  1. Stop new jobs from targeting the old labels.
  2. Wait for active and queued self-hosted jobs to finish.
  3. Reduce the fleet to zero and watch for jobs still requesting old labels.
  4. Remove stale runner registrations and runner groups from GitHub.
  5. Revoke registration tokens, cloud credentials, instance roles, certificates, and VPN identities used by the fleet.
  6. Remove autoscaling groups, controllers, images, disks, caches, networking rules, and monitoring that exist only for the old runners.

Keep logs and audit records for the retention period your organization requires. Don't leave a dormant runner group or machine image with valid credentials behind.

Runner migration versus CI migration

Switching from self-hosted runners to Depot GitHub Actions runners changes the compute beneath each job. GitHub still parses the workflow, schedules jobs, distributes secrets, and reports results.

If the goal is to remove GitHub Actions from the execution path, use Depot CI. Depot CI runs existing GitHub Actions workflow files on Depot's own scheduler and execution engine. That's a CI engine migration, not a runner migration, and it should be evaluated separately from the label-only move described here.

Start building with Depot
in minutes