Agents and Runners: Put the Job Where It Can Safely Run
Choose Azure Pipelines or GitHub Actions and size hosted or self-hosted execution for tools, networking, licenses, and untrusted code.
The recipe does not choose the kitchen
A pipeline defines work; an agent or runner supplies the machine that performs it. A perfect YAML file cannot reach a private database if its execution host has no authorized network path. Equally, a powerful persistent runner is dangerous when untrusted PR code can execute beside valuable credentials.
Choose the delivery platform based on existing governance, source hosting, environments, reusable automation, reporting, and operator skills. Azure Pipelines can build GitHub repositories through a configured integration. GitHub-hosted source does not force the team to move all CI to Actions.
| Execution choice | Strength | Responsibility or risk |
|---|---|---|
| Hosted execution | Managed images and fresh hosted job environments | Image changes, quotas, connectivity constraints, usage charges |
| Self-hosted | Custom tools, private networking, specialized hardware | Patching, cleanup, isolation, credentials, capacity |
| Ephemeral self-hosted | Custom environment recreated for limited work | Provisioning and disposal must actually be automated |
Different hosted options have different capabilities and billing. Verify current limits rather than memorizing a universal free-minute allowance.
Design from the jobโs requirements
List OS, architecture, tool versions, package access, secret scope, target network, expected duration, and parallelism. Include commercial build-tool license obligations. A tool being installed does not prove the organization may run unlimited concurrent copies.
An Azure Pipelines hosted job can select an image:
pool:
vmImage: ubuntu-latest
A self-hosted job can select a pool and capability demand:
pool:
name: SandboxBuildPool
demands:
- node.js
These are alternative fragments, not two pools for one job. Demands match agent capabilities; they do not install missing software or grant permissions.
GitHub Actions selects runners using runs-on, such as a hosted label or an approved self-hosted label set:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- run: echo "Runner selection check"
Before using a self-hosted label, verify which machines can receive it. A label is scheduling metadata, not proof of isolation.
Implement a sandbox runner deliberately
For Azure Pipelines, create a dedicated agent pool, authorize only intended pipelines, and follow the current agent download/configuration instructions for the OS. Use a dedicated low-privilege service account, not an interactive administrator account. Inspect capabilities and run a harmless diagnostic job before granting target access.
For GitHub, create a runner at the intended repository or organization scope, apply runner-group access where available, and follow the generated registration procedure. Registration tokens and platform access tokens have distinct purposes; do not hardcode either into images or source files.
Persistent runners can retain workspaces, credentials, and malicious modifications. Cleaning a checkout is not the same as rebuilding the machine. Keep untrusted fork PRs away from privileged self-hosted infrastructure and sensitive deployment identities.
Connect Azure Pipelines to GitHub
Use the documented GitHub connection method, commonly the Azure Pipelines GitHub App. Install/authorize it only for intended repositories, create a pipeline from the selected repository, and confirm the YAML path and default branch.
Open a sandbox PR and inspect which event triggers a run and what status returns to GitHub. Repository permission, app installation, pipeline authorization, and required status-check configuration are separate controls. A user who can browse the repository may still lack permission to authorize the integration.
For private-network deployment, do not disable firewalls to accommodate a hosted job. Evaluate approved private networking, a scoped deployment runner, or a supported service-specific deployment path. Keep build execution separate from production access when possible.
Size capacity from waiting as well as running
Available machines are not the only concurrency constraint. Azure Pipelines parallel-job entitlement can limit simultaneous execution even when idle agents exist. In GitHub, concurrency settings, organization limits, runner availability, and job dependencies can also affect queueing.
Measure queue time, job duration, utilization, failure rate, and cost per useful run. Increasing runners does not accelerate a dependency chain that is deliberately serial. Caching helps repeated downloads but must have safe keys and must not mix untrusted and privileged state.
Several self-hosted Azure Pipelines agents are idle, but additional eligible jobs remain queued. What else should you check?
Try it: prove runner selection
Prerequisites: a sandbox CI project with permission to run a harmless job. Hosted minutes and VM use may be billed.
Run a job that prints only OS and tool versions, not environment variables or credentials. Verify the selected pool/runner in the run record. Write a design for a private-target deployment showing network path, identity, runner scope, and cleanup. You need not create a VM to complete the design comparison.
Expected observation: scheduling evidence identifies the actual execution host class. A network design still requires a separate authorized connectivity test.
Cleanup: remove the sandbox runner registration and stop/delete only exercise infrastructure you created, following its lifecycle process. Revoke exercise registration credentials where appropriate; do not unregister shared agents.
Decision check: can fork code use the deployment runner?
Treat fork-controlled code as untrusted. Do not expose a persistent privileged runner or production secrets merely because a workflow file exists in your repository.