Move GitHub Actions jobs to Ubicloud runners
Try Ubicloud on one workflow, check images and caching, and compare cost per build. Use a repository variable to switch back without a commit.
From GitHub Actions (hosted runners) to Ubicloud (GitHub Actions runners) · Published 2026-10-08
Before you commit
- Hands-on effort
- Half a day for one repo; 1–2 weeks across a larger organization
- Elapsed time
- Pilot the same commits for a week before rolling out, and keep the old runner path available.
- Examples cover
- Ubuntu 24.04 runner examples · GitHub Actions
- Content review
- 2026-10-09
- Integration testing
- Not recorded. Rehearse the commands and rollback on staging.
Prerequisites
- GitHub administration, permission to use self-hosted runners, an Ubicloud account and a low-risk pilot repo.
- Measured job durations, cache behavior, cloud OIDC policies and network allowlists.
Don’t migrate yet if…
- Your jobs fit within GitHub's free allowance or need Windows, macOS or unsupported runner features.
- Your security policy does not allow code and secrets on third-party self-hosted runners.
GitHub-hosted runners for private repositories cost $0.006 a minute for the standard 2-core Linux machine, after the plan’s included minutes. Ubicloud runs the same kind of job on a 2 vCPU VM in Germany for $0.00125 a minute on standard runners, or $0.002 on premium runners, which are the default for new accounts. Your workflows, secrets and logs stay on GitHub. Only the machine changes. Before you start, plug your own minutes into the calculator.
Changing the runner label takes minutes. Checking tool versions, caches, network access and security takes longer. Use a pilot repo to measure cost per completed build before moving the rest.
0. Before you start
- Measure what you use. In your organization’s Settings → Billing and licensing → Usage, filter by Actions and group by repository and SKU. For a breakdown by workflow and job, use Actions usage metrics under the organization’s Insights tab.
- Note what you get free. The included minutes (2,000 on Free, 3,000 on Pro and Team, 50,000 on Enterprise Cloud) apply only to GitHub-hosted runners. If you stay under them today, moving costs you money.
- Leave public repositories alone. Standard GitHub-hosted runners are free for public repos. Moving them adds a bill and a security question (step 7).
- Check the self-hosted runner policy. To GitHub, Ubicloud runners are self-hosted runners. If your organization or enterprise disables self-hosted runners, or uses an IP allow list, jobs won’t start.
GitHub announced a $0.002/minute platform charge for self-hosted runners from 1 March 2026, then postponed it in December 2025 with no new date. GitHub’s billing docs still say self-hosted runner usage is free. If GitHub brings the charge back, it would apply to Ubicloud minutes as well.
1. Install the Ubicloud GitHub App
- Create an account in the Ubicloud console and add a credit card under Billing. Every account gets a $2.50 monthly credit.
- Open GitHub Runners → Connect New Account. GitHub asks you to install the Ubicloud Managed Runners app.
- Choose Only select repositories and pick one low-risk repository. You can add more later in the organization’s Settings → GitHub Apps.
Premium runners are on by default for new installations. They cost $0.002/min at 2 vCPU instead of $0.00125, and fall back to standard machines when premium capacity is full. To pay the standard rate, switch them off under GitHub Runners → Settings. Run both for a week and compare job times before you decide.
2. Map the labels
| GitHub label | Ubicloud label | Notes |
|---|---|---|
ubuntu-latest, ubuntu-24.04 |
ubicloud-standard-2 |
2 vCPU, 8 GB, Ubuntu 24.04 |
ubuntu-22.04 |
ubicloud-standard-2-ubuntu-2204 |
|
ubuntu-26.04 |
ubicloud-standard-2-ubuntu-2604 |
|
ubuntu-24.04-arm |
ubicloud-standard-2-arm |
2 vCPU, 6 GB |
ubuntu-22.04-arm |
ubicloud-standard-2-arm-ubuntu-2204 |
|
| Larger runner, 4/8/16 cores | ubicloud-standard-4, -8, -16 |
GitHub’s larger-runner labels are names your org chose |
| Larger runner, 32+ cores | ubicloud-standard-30 |
30 vCPU is the largest size |
ubuntu-slim (1 vCPU) |
none | Smallest Ubicloud size is 2 vCPU; keep it on GitHub |
windows-*, macos-* |
none | Linux only; these stay on GitHub |
The runner types page has the full pattern: ubicloud-standard-{vcpu}[-arm][-ubuntu-{version}], with 2, 4, 8, 16 or 30 vCPU. At Ubicloud’s prices, try 4 or 8 vCPU on jobs that parallelize. A bigger machine often makes the build cheaper as well as faster.
3. Add a switch, then change one workflow
Use a repository or organization variable so you can move jobs back without a commit:
# before
jobs:
test:
runs-on: ubuntu-latest
# after
jobs:
test:
runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }}
Then set the variable for the pilot repository:
gh variable set RUNNER --body ubicloud-standard-2 --repo your-org/your-repo
If the variable isn’t set, the job falls back to ubuntu-latest. For a side-by-side comparison, run one workflow on both labels with a matrix:
strategy:
matrix:
runner: [ubuntu-latest, ubicloud-standard-2]
runs-on: ${{ matrix.runner }}
4. Check the image
Ubicloud builds its x64 images from GitHub’s own actions/runner-images Packer templates. Since GitHub started publishing arm64 templates in June 2026, it builds arm64 images from them too. Images are updated monthly, a few days after GitHub’s release, so you get the same tools, sometimes a version behind. Check:
- Pinned tool versions. Use
actions/setup-node,setup-python,setup-goand similar instead of relying on whatever version is preinstalled. - Disk and memory.
ubicloud-standard-2has a 75 GB disk and 8 GB RAM. The arm64 sizes have less memory (6 GB at 2 vCPU). - Docker.
services:containers,container:jobs anddocker buildrun as on GitHub. Ubicloud pulls through a Google Container Registry mirror to avoid Docker Hub rate limits. - Network. Runners have public IPv4 addresses in Germany. If a database, registry or API allowlists GitHub’s IP ranges, add Ubicloud’s ranges. Expect extra latency to US-only services.
If something is missing, install it in a step. Ubicloud’s ubicloud/ssh-runner action lets you SSH into a failing job to see why.
5. Caching
Leave your cache steps as they are. Ubicloud’s transparent cache is on by default and serves actions/cache, the built-in caching in actions/setup-*, Swatinem/rust-cache, and Docker layer caching with cache-from: type=gha / cache-to: type=gha. Each repository gets 30 GB. The oldest entries are evicted first, and entries unused for seven days are deleted.
The cache is separate from GitHub’s, so the first run on Ubicloud starts cold. Don’t time the pilot on that run. The older ubicloud/cache actions are deprecated.
6. arm64 and concurrency
If you build arm64 images under QEMU emulation today, move that job to ubicloud-standard-*-arm, set the platform to linux/arm64, and remove the docker/setup-qemu-action step.
Ubicloud doesn’t publish a concurrency limit for runners. If one push fans out into a large matrix, ask support what your project’s limit is before you move it. Ubicloud can also set up custom runner labels with a concurrency cap for specific workflows. You can’t create them in the console, so ask support.
7. Security
- Ubicloud sees your code and secrets. Every job’s checkout and every secret it uses are on a VM Ubicloud operates. Review its security page and DPA as you would for any CI vendor. Each job gets a fresh VM, and its disk is deleted afterwards.
- Public repositories and forks. GitHub’s guidance is that self-hosted runners should almost never be used for public repositories. Ephemeral VMs reduce the risk, but fork pull requests still run on your bill. If you must use Ubicloud there, require approval for fork workflows in Settings → Actions → General, and keep the cache branch protection that Ubicloud enables by default.
- OIDC to AWS, GCP or Azure still works, because GitHub issues the token, not the runner. If a cloud trust policy checks the
runner_environmentclaim, its value changes fromgithub-hostedtoself-hosted. Update the policy.
8. Verify
Run the pilot workflow for a week. Compare on the same commits:
- Job duration in the Actions run summary, ignoring cold-cache runs.
- Cost per build: duration × per-minute price for each job, summed for the workflow. A 2 vCPU job that takes 10 minutes on GitHub costs $0.06. On Ubicloud standard it costs $0.0125, or $0.02 on premium. If
ubicloud-standard-8finishes it in 4 minutes, that’s $0.02 on standard. - Queue time: how long jobs wait before starting. Larger sizes can queue at peak times.
- Failure rate: a test that fails on one side only points to an image or network difference.
Then add repositories to the app, set RUNNER at the organization level, and move the busiest workflows first.
9. Rollback plan
Delete or change the variable:
gh variable delete RUNNER --repo your-org/your-repo
The next run uses ubuntu-latest again. No commit or revert is needed. Queued jobs that already requested a Ubicloud label still run there.
10. Clean up
-
Once the switch has been stable for a few weeks, you can hard-code labels instead of using the variable. Swap them in bulk, and review the diff before committing. This only touches lines that are exactly
runs-on: ubuntu-latest:find .github/workflows -name '*.y*ml' -exec \ sed -i -E 's/^([[:space:]]*runs-on:[[:space:]]*)ubuntu-latest[[:space:]]*$/\1ubicloud-standard-2/' {} + git diffIt skips matrix entries,
runs-onarrays and reusable workflows, so grep forubuntu-afterwards. -
Keep
windows-*,macos-*andubuntu-slimjobs on GitHub. Ubicloud doesn’t offer them. -
Set an Actions budget in GitHub so a workflow you missed can’t run up a bill on GitHub-hosted runners.
Found a command or version mismatch? Report a correction with the version and steps to reproduce it. Remove secrets and customer data first.