When you are just starting out with Azure DevOps, the managed Microsoft-hosted agents seem like the easy choice. Clean environment, no setup, just works.
Then you hit the parallelism limit on a free plan and everything queues.
That is what sent me down the path of setting up my own self-hosted agent. Here is exactly what I built and what tripped me up along the way.
What a Self-Hosted Agent Actually Is
An agent is the thing that executes your pipeline jobs. When Azure DevOps runs a pipeline, it needs a machine to do the work.
Microsoft-hosted agents are temporary VMs that spin up, do the job, and disappear. Convenient but limited.
A self-hosted agent is one you run yourself, on infrastructure you control. It stays running between jobs, keeps its installed software, and has no parallelism limits.
What I Built
- Ubuntu 22.04 VM on Azure (Standard_D2s_v3)
- A custom agent pool called SelfHostedPool
- Azure Pipelines agent v4.271.0 installed as a system service
- A test pipeline that confirmed everything was working
Step 1: Create a Personal Access Token
In Azure DevOps, click your profile picture at the top right, then go to Personal Access Tokens. Create a new one with:
- Agent Pools: Read and Manage
- Build: Read and Execute
Copy it immediately. You will not see it again.
Step 2: Create the Agent Pool
Go to Organization Settings, then Pipelines, then Agent Pools. Click Add Pool.
- Type: Self-hosted
- Name: SelfHostedPool
- Grant access to all pipelines: checked
Step 3: Provision the VM
I created a Standard_D2s_v3 Ubuntu 22.04 VM on Azure via the portal. The key thing is opening port 443 for outbound communication. The agent talks to Azure DevOps over HTTPS.
Step 4: Get the Agent Download URL
This is where I ran into my first real obstacle. The standard download domain was not resolving. The fix was to get the download URL directly from the Azure DevOps API:
curl -s "https://dev.azure.com/YOUR_ORG/_apis/distributedtask/packages/agent/linux-x64?top=1&api-version=3.0" \
-u :YOUR_PAT | grep "downloadUrl"
This gives you the exact URL for the latest agent version registered for your organization. Use that URL to download.
Step 5: Install and Configure
mkdir -p ~/azagent && cd ~/azagent
curl -fSL "DOWNLOAD_URL_FROM_ABOVE" -o vsts-agent-linux-x64-4.271.0.tar.gz
tar zxvf vsts-agent-linux-x64-4.271.0.tar.gz
./config.sh
When prompted, enter your organization URL, PAT, and pool name. Then start it as a service:
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status
Active and running.
Step 6: Verify in Azure DevOps
Go to Organization Settings, then Agent Pools, then SelfHostedPool. Your agent should appear as Online.
The Test Pipeline
trigger:
- main
pool:
name: SelfHostedPool
steps:
- script: uname -a
displayName: System Info
- script: whoami
displayName: Current User
- script: df -h
displayName: Disk Usage
Run it. Watch all three steps execute on your VM. That green checkmark means your pipeline is running on infrastructure you built.
What I Learned
Running as a system service is not optional for real use. If you run the agent interactively with ./run.sh, it dies when your SSH session ends. Install it as a service so it runs permanently.
Port 443 must be open outbound, not inbound. The agent calls Azure DevOps, not the other way around.
The API download method is more reliable than guessing version numbers. Always use it.
This agent pool powered every pipeline in my Azure DevOps series. Projects 2, 3, and 4 all ran on it. Building it first was the right call.
Vivian Chiamaka Okose is a DevOps Engineer documenting her learning journey in public.


















Top comments (0)