GitHub Actions vs Azure Pipelines: two Microsoft billing models
GitHub Actions vs Azure Pipelines is a comparison of two billing models from the same company: Actions sells minutes and caps how many jobs run at once by plan, while Azure Pipelines sells the concurrency itself at $40 a month per Microsoft-hosted parallel job and then stops counting minutes. Which is cheaper depends entirely on whether your problem is volume or width.

Both are Microsoft products and both run YAML pipelines on hosted or self-hosted machines, so the feature comparison people expect is mostly a wash. The thing that actually differs, and the thing that decides real bills, is the unit each one sells.
GitHub Actions bills per runner minute, gives every plan an included allowance, and sets a job concurrency ceiling per plan: 20 concurrent jobs on Free, 40 on Pro, 60 on Team, 500 on Enterprise Cloud. Azure Pipelines bills per parallel job, which Microsoft's own documentation compares to a lane in a swimming pool, and on the paid tier has no monthly minute limit at all.
Every rate and limit below was read from the Azure DevOps pricing page, from the Microsoft Learn page on configuring and paying for parallel jobs, and from GitHub's billing and limits references on 21 September 2026. One of them carries a retirement notice that changes the answer for open source, and it is covered in its own section.
GitHub Actions against Azure Pipelines, on the axes that move a bill (read 21 September 2026)
| GitHub Actions | Azure Pipelines | |
|---|---|---|
| What you buy | Runner minutes, at $0.006 a minute for Linux at 2 vCPU | Parallel jobs. $40 a month per extra Microsoft-hosted job with unlimited minutes, $15 for a self-hosted one |
| Free tier | 2,000 minutes a month on Free, 3,000 on Team, 50,000 on Enterprise Cloud, and free and unlimited on public repositories | One Microsoft-hosted job capped at 60 minutes per run and 1,800 minutes a month, which you have to enable by linking an Azure subscription, plus one self-hosted job with unlimited minutes granted automatically |
| Job time limit | 6 hours on GitHub-hosted runners, 5 days on self-hosted | 60 minutes on the free tier, 360 minutes on the paid tier, none on self-hosted |
| Concurrency | Capped by plan: 20, 40, 60 or 500 concurrent jobs, raisable through support | Exactly what you bought. New organizations are capped at 25 Microsoft-hosted parallel jobs until support raises it |
| Public and open source | Standard GitHub-hosted runners are free and unlimited on public repositories | Public projects are retired. New ones cannot be created, and existing ones convert to private starting in 2027, after which the 1,800-minute private allocation applies |
| Where jobs run | GitHub's fleet, a managed vendor's fleet, or self-hosted runners | Microsoft-hosted agents, self-hosted agent pools, or Azure DevOps Server on your own hardware |
| Self-hosting economics | Self-hosted runners are free to register; you pay for the machines | $15 a month per self-hosted parallel job on Azure DevOps Services. On Azure DevOps Server there is no charge for self-hosted concurrency: the number of agents is the only limit |
| Self-healing or retries | None on GitHub-hosted runners; transient failures diagnosed and retried inside the run on Latchkey | None. A failed job is re-queued into the same lane it was already waiting in |
| Setup change required | A file in .github/workflows, discovered automatically from the repository | azure-pipelines.yml, plus an organization, a project and a billing relationship set up against an Azure subscription |
The arithmetic, which is the whole comparison
A Microsoft-hosted parallel job is $40 a month and has no monthly minute limit. At GitHub's $0.006 a minute, $40 buys 6,667 Linux minutes. So a single lane pays for itself the moment you push more than about 6,700 minutes a month through it, and a lane has roughly 43,200 minutes in a 30-day month to give.
Run that both ways on the same month. Ten thousand Linux minutes on GitHub Actions, private repository, on a Team plan: 3,000 are included, 7,000 bill at $0.006, so $42. The same 10,000 minutes on Azure Pipelines through one paid lane: $40, and that lane sits idle 77% of the month. Azure wins by two dollars and loses on queueing, because every job beyond the first waits.
Now hold concurrency fixed instead of volume. GitHub Team includes a ceiling of 60 concurrent jobs at no additional charge, and you pay only for the minutes you actually use. Matching 60 Microsoft-hosted lanes on Azure costs 59 paid lanes on top of the free one, which is $2,360 a month whether or not anything runs. That is the trade in one number: Azure charges for the width of the pipe, GitHub charges for the water.
Three things on the Azure side that catch people out
The first purchase does not add a lane. Microsoft states it plainly: when you buy your first Microsoft-hosted parallel job, the number of parallel jobs in the organization is still one, and the purchase only removes the time limits on that first job. To run two jobs at once from the free tier you buy two.
The free tier is not automatic. The Microsoft-hosted free tier has to be enabled by linking the organization to a valid Azure subscription and setting up billing; only the self-hosted grant is automatic. Teams that expected 1,800 free minutes on signup and got none usually skipped that step.
New organizations are capped. Microsoft limits new organizations to 25 parallel jobs for Microsoft-hosted agents, with increases granted by support and subject to regional capacity. If your plan is to buy width rather than optimize, check that ceiling before you build the business case.
The public projects retirement, which changes the open source answer
This is the largest difference between the two platforms today and it is easy to miss, because it lives in a notice rather than on the pricing table. Microsoft has retired public projects in Azure DevOps: new public projects can no longer be created, and existing public projects convert to private starting in 2027. The Learn page adds that the same free-tier allocation, one job at 1,800 minutes a month, applies to existing public projects once they convert.
GitHub's position on the same question is the opposite and is stated in its billing reference: standard GitHub-hosted runners are free and unlimited for public repositories. For a maintainer, that is not a rate difference, it is a difference between a bill and no bill.
If you run an open source project on Azure DevOps today, the retirement notice is the reason to plan a move, and it sets the deadline for you.
Speed
Not benchmarked yet. Both vendors publish agent specifications rather than timings, and the runner benchmark report will carry a measured comparison when it exists.
What can be said without a benchmark is where the wall clock actually goes on each platform, and it is not the same place. On GitHub Actions, a slow pipeline is usually a slow job: the concurrency ceiling on Team is 60 and most repositories never approach it. On Azure Pipelines the common complaint is queue time, because the lanes are a number somebody bought, and Microsoft's own sizing advice is a rule of thumb rather than a measurement: estimate one parallel job for every four to five users in the organization.
Why there is no recorded run on this page
The quantity that decides this comparison is not a job, it is a month of concurrency. A single recorded run would report one duration on one agent, and duration is the input the reader already has from their own history. What matters here is how many of those runs want to start at the same moment, and that is a fact about your team rather than about either platform. The rates and limits it is priced against are published, dated and linked below.
When each one is right
- Azure Pipelines when the code is in Azure Repos, when a multi-stage release with approval gates and environments is the product you actually bought, or when Azure DevOps Server on your own hardware is the compliance answer, where self-hosted concurrency carries no charge at all.
- Azure Pipelines when your CI is high-volume and narrow: a handful of lanes running most of the day beats metered minutes, and the paid tier stops counting.
- GitHub Actions when the code is on GitHub, because the pull request, the required checks and the merge queue are the same object as the pipeline.
- GitHub Actions for anything public, where standard runners are free and unlimited and Azure DevOps no longer offers public projects at all.
- Neither switch when the real complaint is the runner rather than the platform. Moving a pipeline between two Microsoft CI products to save on compute is a large change for a small saving; changing what the job runs on is one line.
Switching: the exact diff
The translation is mechanical for simple pipelines. Stages become jobs, pool becomes runs-on, script becomes run, and trigger becomes on. What does not translate is anything that depended on the parallel-job model, because on the GitHub side the equivalent of buying a lane is simply running more jobs and being billed for their minutes.
-# azure-pipelines.yml
-trigger:
- - main
-pool:
- vmImage: ubuntu-latest
-steps:
- - script: npm ci && npm test
+# .github/workflows/ci.yml
+on:
+ push:
+ branches: [main]
+jobs:
+ test:
+ runs-on: ubuntu-latest # or latchkey-small at $0.0025 a minute
+ steps:
+ - uses: actions/checkout@v4
+ - run: npm ci && npm testThe verdict
Stay where your code is, and then argue about the bill. If the repositories are in Azure Repos and the release process is a multi-stage pipeline with approval gates, Azure Pipelines is a mature product and the $40 lane is a fair deal for high, steady volume. If the code is on GitHub, Actions is not just the native option, it is the cheaper one at almost every width, because GitHub gives 60 concurrent jobs on Team for nothing and charges only for minutes used, while matching that concurrency on Azure is $2,360 a month standing. For open source the argument is already over: Azure DevOps has retired public projects, and GitHub-hosted standard runners remain free and unlimited on public repositories.