Deploying Multiple Self-Hosted DevOps Agents Using Docker
This guide walks through containerizing Azure DevOps agents to create scalable, isolated build environments. You'll learn how to:
- Build a custom Docker image with pre-installed tools (PowerShell, Bicep, Git, etc.)
- Run multiple agents in containers for faster CI/CD pipelines
- Automate agent registration using startup scripts
Prerequisites
- A Linux/macOS/WSL or Windows system with:
- Docker installed
- An Azure DevOps organization
- A Personal Access Token (PAT) with Agent Pools (Read & manage) scope
Azure DevOps Parallel Job Limits
While you can register an unlimited number of self-hosted agents, private projects are limited to 1 free parallel job by default. This means that you can only run one job at a time unless you purchase more.
Even if only one job runs at a time, having multiple agents ensures you have failover options if one agent goes down, and you’re prepared to scale up when you need to.
Project Type |
Agent Type | Free Tier (Default) | Notes |
Private | Microsoft-hosted | 1 parallel job | Can purchase more minutes or parallel jobs |
Up to 1,800 minutes/month | |||
Self-hosted | 1 free parallel job | +1 additional if you have a Visual Studio Enterprise subscription | |
Total: 2 parallel jobs | You can purchase additional self-hosted parallel jobs | ||
Public | Microsoft-hosted | Up to 10 parallel jobs | Provided automatically for open source/public repositories |
Self-hosted | Unlimited parallel jobs | No cost; ideal for open-source/public projects |
Folder Structure
azdo-agents/
├── Dockerfile
├── start.sh
├── docker-compose.yml
├── .env1
├── .env2
├── .env3
├── .env4
Before you begin, make sure you have:
- Created the Agent Pool in Azure DevOps (Project Settings → Agent Pools → Add Pool)
- Generated a PAT token with 'Agent Pools (Read & Manage)' permissions
Create the Dockerfile
Create a file called Dockerfile
the following content:
Tooling Installation:
PowerShell
- Installs the latest stable release via the Microsoft package repo
Key Components:
Azure DevOps Agent v4.255.0
- Downloads and extracts the specified agent versionUbuntu 24.04
- Base container image with essential dependenciesNon-root user
- Dedicated 'azdo' user with restricted sudo accessPandoc 3.6.4
- Downloads and installs a specific .deb package for documentation processingBicep CLI
- Installs the latest Linux binary for ARM template deployments
You can add further tools based on your needs..
Dockerfile
FROM ubuntu:24.04
# Install dependencies
RUN apt-get update && \
apt-get install -y \
apt-utils \
curl \
wget \
sudo \
jq \
unzip \
git \
libcurl4 \
libunwind8 \
gettext \
apt-transport-https \
ca-certificates \
gnupg \
software-properties-common && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Install PowerShell
RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg && \
install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/ && \
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-jammy-prod jammy main" > /etc/apt/sources.list.d/microsoft.list' && \
apt-get update && \
apt-get install -y powershell && \
rm microsoft.gpg && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Install Pandoc
RUN wget https://github.com/jgm/pandoc/releases/download/3.6.4/pandoc-3.6.4-1-amd64.deb -O /tmp/pandoc.deb && \
apt install -y /tmp/pandoc.deb && \
rm /tmp/pandoc.deb
# Install Bicep CLI
RUN curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64 \
&& chmod +x ./bicep \
&& mv ./bicep /usr/local/bin/bicep
# Create non-root user
RUN useradd -m azdo
# Copy script and fix permissions
WORKDIR /home/azdo
COPY start.sh ./start.sh
RUN chmod +x ./start.sh && chown azdo:azdo ./start.sh
# Grant azdo user passwordless sudo access
RUN echo "azdo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Switch to azdo user
USER azdo
# Download the agent
ARG AZP_AGENT_PACKAGE_URL=https://vstsagentpackage.azureedge.net/agent/4.255.0/vsts-agent-linux-x64-4.255.0.tar.gz
RUN curl -LsS $AZP_AGENT_PACKAGE_URL | tar -xz
ENTRYPOINT ["./start.sh"]
Create the start.sh
Script
#!/bin/bash
set -e
if [ -z "$AZP_URL" ] || [ -z "$AZP_TOKEN" ] || [ -z "$AZP_POOL" ]; then
echo 1>&2 "Missing AZP_URL, AZP_TOKEN or AZP_POOL environment variable"
exit 1
fi
cleanup() {
echo "Cleanup. Removing Azure Pipelines agent..."
./config.sh remove --unattended --auth pat --token "$AZP_TOKEN"
}
trap 'cleanup; exit 0' SIGINT SIGTERM
AZP_AGENT_NAME=${AZP_AGENT_NAME:-$(hostname)}
echo "1. Configuring agent..."
./config.sh --unattended \
--url "$AZP_URL" \
--auth pat \
--token "$AZP_TOKEN" \
--pool "$AZP_POOL" \
--agent "$AZP_AGENT_NAME" \
--acceptTeeEula \
--replace
echo "2. Running agent..."
./run.sh
Create multiple .env Files
This keeps secrets out of your Dockerfile and helps with reusability. Replace the values with your org
.env.template
AZP_URL=https://dev.azure.com/your-org
AZP_TOKEN=your-pat-token
AZP_POOL=Default
AZP_AGENT_NAME=docker-agent-1
Copy the template file to multiple files
cp .env.template .env1
cp .env.template .env2
cp .env.template .env3
cp .env.template .env4
Make sure to edit the .env files with different agent Names e.g.
AZP_AGENT_NAME=docker-agent-01
AZP_AGENT_NAME=docker-agent-02
AZP_AGENT_NAME=docker-agent-03
AZP_AGENT_NAME=docker-agent-04
Build the Docker Image
docker build -t azdo-agent .

Create a Docker Compose file
Here you will define the containers and how many you want.
docker-compose.yml
services:
agent1:
image: azdo-agent
container_name: azdo-agent-1
env_file: .env1
restart: unless-stopped
agent2:
image: azdo-agent
container_name: azdo-agent-2
env_file: .env2
restart: unless-stopped
agent3:
image: azdo-agent
container_name: azdo-agent-3
env_file: .env3
restart: unless-stopped
agent4:
image: azdo-agent
container_name: azdo-agent-4
env_file: .env4
restart: unless-stopped
Launch All Agents with Docker Compose
This command will create all the containers you've defined in the file above
docker compose up -d
You should see the below

Use docker ps to validate they are running

Monitor Logs
To see logs for a specific agent:
docker compose logs -f azdo-agent-5

Tear Down
You can remove the agents with the following commands if you no longer require them
docker compose down

Validate
You can verify the agents are running by navigating to your Azure DevOps project → Project Settings → Agent pools, selecting the correct pool, and checking the agent status under the Agents tab.

Optional: Rebuild and Restart
If you change the Dockerfile or start.sh
:
docker compose build --no-cache
docker compose up -d
You can clone the files here:
https://github.com/phipcode/phiptechblog/tree/main/azuredevops/docker-agents
Found this article useful? Why not buy Phi a coffee to show your appreciation?