Where GitHub Actions and Azure DevOps Pipelines Stand in October 2026
Both platforms are in active development, and both are supported. Microsoft funds them for different jobs, and that is the part worth knowing before you choose.
At Build 2026, new planning, code review, security and agent capabilities landed on GitHub first. Microsoft's own write-up describes continued Azure DevOps investment aimed at security and code quality, with Boards, Pipelines and Test Plans usable alongside GitHub repos (Azure DevOps Blog). Its Copilot, Agents and Platform organization has moved 1,575 repositories from Azure Repos to GitHub Enterprise while keeping Azure Pipelines and Boards in place. Microsoft also ended public projects in Azure DevOps in April 2026 and sends open-source hosting to GitHub.
Running both got cheaper in 2026: Azure DevOps Basic usage rights are included with GitHub Enterprise, so a developer using GitHub for code and Azure Boards for planning needs one seat license.
5 questions drive the decision:
- How is the pipeline written, and how is it reused across repos?
- Where do jobs run, and what private networks must runners reach?
- Do you want to pay per minute or per concurrent lane?
- How does a job authenticate to your cloud, and what can it prove about what it built?
- What evidence will an auditor ask for, and which system holds it if you run both?
What Sits in Each Product
GitHub Actions is one feature of GitHub. Azure DevOps is a suite of 5 services and Azure Pipelines is one of them. Microsoft's docs call the service Azure Pipelines, and this post treats the two names as the same thing.
For a DevOps team the comparison that matters is those 2 engines. Azure DevOps also brings Boards, Repos, Test Plans and Artifacts; GitHub also brings Issues and Projects, Packages, Codespaces, Dependabot and code scanning. Those get a short section after the cost numbers.
Writing the Pipeline
Both use YAML and both look familiar for about 20 lines. The same Python build on each:
Azure Pipelines
trigger:
branches:
include: [ main ]
pool:
vmImage: ubuntu-24.04
stages:
- stage: build
jobs:
- job: test
strategy:
matrix:
py311:
pythonVersion: '3.11'
py312:
pythonVersion: '3.12'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: $(pythonVersion)
- script: |
python -m pip install --upgrade pip
pip install -e '.[test]'
pytest --junitxml=junit.xml
displayName: Run tests
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFiles: junit.xml
GitHub Actions
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- run: |
python -m pip install --upgrade pip
pip install -e '.[test]'
pytest --junitxml=junit.xml
4 differences show up once the file grows past this.
Checkout
Azure Pipelines clones the repo for you. Actions does nothing until you call actions/checkout. Teams moving from Pipelines hit this on day one.
Stages
Azure Pipelines has a stages keyword with its own approvals and dependencies. Actions builds the same behavior from jobs, needs and environment gates, in more lines.
Expressions
Azure Pipelines uses 3 syntaxes: $(var) for macros, ${{ }} at compile time, $[ ] at runtime. Actions uses ${{ }} for everything. One syntax is easier to teach; three give you template logic that runs before the job is scheduled, which is how extends templates enforce policy.
Reuse
Azure Pipelines reuses step, job and stage templates with typed parameters, and an organization can require every pipeline to extend an approved template. Actions reuses through composite actions and reusable workflows, capped at 50 unique workflows per file including nested trees.
Pinning
Actions pins third-party code by Git ref, and first-party major tags moved more than once through 2026 as the runner image moved to Node 24. The versions above are current as of October 2026. Pin by full commit SHA and let Dependabot raise the bumps.
Runners and Agents
The hosted machines offer much the same thing: Ubuntu, Windows Server and macOS images with common toolchains baked in, thrown away after the job. A GitHub-hosted job can run 6 hours; Azure Pipelines caps a Microsoft-hosted run at 60 minutes on the free tier and 360 on a paid parallel job.
Self-hosting is where they diverge. GitHub runners register at repository, organization or enterprise level, and Actions Runner Controller runs them as Kubernetes pods scaled by webhook. Azure DevOps gives you 3 options: a plain agent on a VM, virtual machine scale set agents against a VMSS you own, or Managed DevOps Pools, where Microsoft runs the scaling and you supply the image and the network.
What the runner must reach usually decides this before price does. Four things push a pipeline onto self-hosted capacity whichever platform you pick:
- Private endpoints A job running migrations against a database on a private endpoint, or restoring from an internal Nexus or Artifactory, needs an agent inside the VNet.
- Egress allowlistsHosted runners arrive from published ranges that change. GitHub lists its ranges at api.github.com/meta and Microsoft republishes the Azure ranges weekly. Tracking those lists is permanent maintenance; a self-hosted pool behind one NAT gateway address removes it.
- License servers and attached hardware. Floating licenses and physical devices need a machine you control.
- Data residency Regulated workloads sometimes cannot execute on a hosted machine in another region.
Managed DevOps Pools is the one managed option offering private VNet access and custom images without you running the autoscaler. On GitHub the nearest equivalent is ARC on a cluster you operate.
2 things to get right on self-hosted capacity either way. Make the runner ephemeral: a runner that keeps its working directory hands the next job the previous one's checked-out source, cached credentials and Docker layer cache, which on a shared pool crosses a boundary your permission model says exists. And give the pool its own subnet with deny-by-default egress, allowlisting your registry, package feeds and cloud control plane. A runner executes whatever YAML arrives in a pull request, with whatever network access you gave it.
Cost: Two Different Billing Units
GitHub Actions bills minutes. Azure Pipelines bills concurrency. That one sentence decides most of the money question.
GitHub cut hosted-runner rates by up to 39% on January 1, 2026. A standard Linux 2-core minute now lists at $0.006, Windows at $0.010, macOS at $0.062, and a 1-core Linux runner at $0.002. Included minutes per month: 2,000 on Free, 3,000 on Pro and Team, 50,000 on Enterprise Cloud. The $0.002-per-minute platform charge GitHub announced for self-hosted runners was postponed within 48 hours and still has no date as of October 2026, so self-hosted minutes cost you nothing from GitHub.
Azure DevOps charges $6 per Basic user per month after the first 5 free. Every organization gets 1 Microsoft-hosted parallel job with 1,800 minutes and 1 self-hosted parallel job with unlimited minutes. Extra lanes cost $40 per hosted job or $15 per self-hosted job per month, both with unlimited minutes.
2 scenarios for a 25-engineer team on private repos, Linux builds, 4 concurrent lanes at peak. Scenario A buys 3 extra Azure lanes and leaves the free lane absorbing 1,800 minutes; scenario B buys 4 paid lanes so nothing hits the free lane's cap:
The crossover is arithmetic you do once. $40 divided by $0.006 is 6,667 minutes, so a lane busier than roughly 6,700 minutes a month costs less as an Azure parallel job. macOS widens the gap: $0.062 a minute on GitHub against the same $40 lane with no cap on Azure, which matters for anyone building iOS daily.
Self-hosting changes it again. Run ARC on a cluster you already own and GitHub's minute meter stops mattering; on Azure DevOps the $15 self-hosted lane still applies per concurrent job, so capacity means buying lanes as well as nodes.
The Rest of the Suite, Briefly
Package feeds and registries sit inside the build. The 3 after them decide which platform an organization lives in.
Package feeds
Azure Artifacts hosts NuGet, npm, Maven, Python and Universal feeds with upstream sources that proxy and cache public registries. GitHub Packages hosts npm, NuGet, Maven, RubyGems and container images but has no upstream-source feature, so builds needing a cached internal mirror keep Azure Artifacts for that feed or put Artifactory or Nexus in front.
Container registry
GitHub Container Registry (ghcr.io) authenticates with the workflow's GITHUB_TOKEN, so a push needs no extra credential. Azure Pipelines pushes to Azure Container Registry through a service connection, ideally a workload identity one.
Work tracking
Azure Boards has configurable work item types, inherited process templates, 4 levels of hierarchy, sprint capacity and WIQL queries over the REST API. GitHub Projects has custom fields, saved views and a GraphQL API, with no work item types or WIQL.
Test management
Azure Test Plans handles manual test cases, exploratory sessions and traceability to requirements. GitHub has no counterpart. The Basic + Test Plans license is $52 per user per month and only testers need it.
Dev environments
GitHub Codespaces gives prebuilt dev containers from a devcontainer.json. Azure DevOps has no equivalent; Microsoft Dev Box is the separate product.
Security and Supply Chain
This is where the two have drifted furthest apart, and it usually decides things for a regulated team. Policy enforcement and audit evidence come under governance below.
Keyless cloud auth
Both support OIDC federation, so a pipeline gets a short-lived Azure or AWS token with no stored secret. On Actions you add permissions: id-token: write and configure a federated credential on the app registration or IAM role; on Azure Pipelines you use a workload identity federation service connection. Do this before anything else. Stored client secrets in variable groups and repository secrets are a recurring finding in the pipeline reviews we run, and OIDC removes the category.
Scanning
GitHub has secret scanning with push protection, Dependabot and CodeQL. GitHub Advanced Security for Azure DevOps is a paid add-on running the same engines, so the capability matches and the remediation features arrive on GitHub first.
Provenance
Actions has actions/attest-build-provenance, producing a signed SLSA attestation tied to the workflow run and verifiable with gh attestation verify. Azure Pipelines has no first-party equivalent; you wire up Sigstore, Notation or SBOM tooling yourself. For anyone working toward SLSA Build Level 3, that is a real gap.
Approvals and gates
Azure Pipelines environments support approvals, business hours windows, REST API gates and Azure Monitor alert queries before a stage proceeds. GitHub environments support required reviewers, wait timers and deployment branch policies, and expect anything automated to be a job in the workflow.
Governance at Scale
Both platforms enforce policy centrally. They enforce different things, and the difference shows up around repo number 50.
Pipeline policy
Azure DevOps organizes as organization → project → repos and pipelines, with permissions on security groups inheriting downward. Its strongest control is extends templates: a pipeline extends an approved template in a locked repo, and the template decides which steps may run, evaluated before the job is scheduled. A developer cannot add a step the template forbids. GitHub organizes as enterprise → organization → repos, with rulesets applying named rules to repo name patterns and required workflows forcing a workflow onto every matching repo. A required workflow runs alongside the developer's workflow, so the control is detective where Azure's is preventive.
Branch rules
Azure Repos sets policies per branch: required reviewers, linked work items, build validation, merge strategy. GitHub rulesets define a set once and apply it across the organization by name pattern, with auditable bypass lists. 1 repository with many protected branches favors Azure's granularity; 200 repositories favor not configuring each by hand.
Audit evidence
Both emit audit logs and both expect you to stream them somewhere you control. GitHub Enterprise Cloud streams to Amazon S3, Azure Blob Storage, Azure Event Hubs, Datadog, Google Cloud Storage and Splunk. Azure DevOps auditing streams to Azure Monitor Logs and Splunk and is off by default. Both keep only a limited window in the service, so turn streaming on the day you create the organization.
What an auditor asks for, and where it comes from:
Row 3 is the one that catches people. Classic release pipelines keep deployment logic in a UI with its own change history, so SOC 2 evidence collection covers 2 systems instead of 1. Converting them to YAML is worth doing whether or not you migrate platforms.
Which Constraint Points Where
Each row names a constraint and the platform that covers it natively today. Most teams tick several that point different ways, which is why the next section describes a split.
How We Run It at Covalience
We run a hybrid, the same shape Microsoft describes for its own teams: code and pipelines on GitHub, planning in Azure DevOps.
Moving the repositories was a deliberate call, made on 2 grounds. Developers work faster in GitHub: pull request review, code search, the CLI and the extension ecosystem all take fewer steps than the Azure Repos equivalents, and that difference compounds across a day. The second ground is release order. New Microsoft capability lands on GitHub first, so staying on Azure Repos meant waiting for features the teams could already see shipping elsewhere.
Boards hold the work items, sprints and delivery reporting our leads need across client engagements, with the process templates and queries that reporting depends on. GitHub carries the repositories and the pipelines, and the AB#123 convention in commit messages ties every commit back to its work item.
4 things we learned running it:
- OIDC for everything that touches a cloud No workflow holds a long-lived credential, which answers most of what an auditor asks about deployment access.
- One repository host, one set of branch rules Reviewers, build validation and linear history are enforced through GitHub rulesets at org level, reviewed quarterly.
- Evidence mostly lands on one side Approvals, status checks, pipeline history and build provenance are all GitHub. Access logging is the only genuinely split record, so both audit streams go into the same SIEM index and get joined there instead of at audit time.
- Budget for the overhead 2 permission models and 2 onboarding paths, paid in platform engineering time more than in licenses.
Keeping repositories on GitHub also means Copilot's coding and review agents work against our code without a second migration. That was not why we chose the split, and it is one reason we have kept it.
The reverse hybrid exists too: GitHub repos with Azure Pipelines driving CI through a GitHub App service connection, which keeps Azure's parallel-job economics and extends enforcement. Watch pull request triggers on forks, which behave differently from pr: triggers against Azure Repos.
How to Run the Decision
Both platforms answer most constraints well, so the work is finding which constraints you actually have. The sequence we use on client assessments:
- Measure before you model Export one month of real CI minutes and peak concurrency per repo. az pipelines runs list and the GitHub Actions usage report both give you CSV. The per-lane against per-minute question flips around 6,700 minutes per lane, and teams guess wrong in both directions.
- Map each pipeline to the networks it touches Private endpoints, IP-allowlisted APIs, license servers, on-prem hosts. Any pipeline with one of those is self-hosted whichever platform you pick, which takes it out of the cost comparison.
- Find the one constraint with no workaround Most rows in the table above have one. Usually a single row does not, and that row decides more than the other 9 together.
- Price the hybrid against the migration Azure DevOps Basic usage rights come with GitHub Enterprise, so the license delta is small. Compare the engineering hours of two permission models against the hours of moving the pipelines, over the same 18 months.
- Pilot on a service with secrets and an approval gate Something that authenticates to a cloud, deploys behind an approval and publishes an artifact, run alongside the existing pipeline until it has shipped to production twice.
If You Are Migrating Azure Pipelines to Actions
If step 3 pointed you at a move, start with GitHub's Actions Importer CLI, which converts Azure Pipelines YAML into workflows and gives you a first draft. Run its audit command across the whole organization first: it reports which tasks have no mapping, and that number tells you how long the project really is.
What converts without trouble: script and bash steps, pool.vmImage to runs-on, simple matrices, artifact publish and download, and most checkout behavior.
What needs rework:
- Marketplace tasks Every task: Something@2 needs a matching action or a shell replacement. Tasks wrapping Azure CLI usually become azure/login plus az commands, which is cleaner anyway.
- Variable groups and secure files These become Actions secrets and variables, scoped at repo, org or environment. Variable groups backed by Key Vault become an azure/login step plus a Key Vault fetch at runtime. Map the scope deliberately; the flattening is where secrets leak into repos that should not see them.
- Service connections Rebuild as OIDC federated credentials. Do not port a client secret across.
- Stages with approvals Each stage becomes a job with needs and an environment, and approvals move to environment protection rules.
- Expression syntax $(var) becomes ${{ env.VAR }} or $VAR depending on whether it is evaluated by the runner or the shell. Runtime expressions $[ ] have no direct equivalent and usually become outputs from a prior job.
- Classic release pipelines No importer handles these. Convert to YAML on Azure Pipelines first, then migrate the YAML.
Plan the parallel-running window, which is the long part. Converting a pipeline with 3 stages and a dozen tasks is quick; proving it takes longer, because the failures that matter appear on a real release. Sequence by risk, lowest first, and keep the old pipeline runnable until the new one has shipped to production at least twice.
We have built and run delivery pipelines on both platforms, including the hybrid above. If you want a second pair of eyes on your estate, our platform team does CI/CD and DevSecOps assessments.









