Azure DevOps Self-hosted Agent on Linux: Install, Register and Run as a Service
À la fin de ce tutoriel, vous saurez choisir self-hosted vs Microsoft-hosted, préparer une machine Linux (Ubuntu 24.04 / 22.04), créer un agent pool, enregistrer un agent Azure Pipelines avec un PAT (placeholder
<PAT>uniquement), le lancer en service systemd, le cibler depuis un YAML, et appliquer une checklist sécurité — sans coller de secrets en clair.Niveau : Intermédiaire · Temps estimé : 60–90 min · Versions testées : Azure DevOps Services (portail 2026), agent Linux x64, Ubuntu 24.04 · Dernière vérification : 2026-09-12
Slug live (menu) :
azure-devops-self-hosted-agents· WP post :1070· URL : https://devopelastichayway.com/azure-devops-self-hosted-agents/ · Série : Azure DevOps · Région lab Azure (si VM) :canadacentral
Prérequis
- Organisation Azure DevOps où vous êtes Project Collection Administrator (ou Administrator sur un agent pool)
- Droits pour créer un PAT avec le scope Agent Pools (Read & manage)
- Machine lab Linux x64 : Ubuntu 24.04 recommandé (22.04 OK avec
libicu70) ou VM Azurecanadacentral(B1s/B2s) — pas de production - Accès sortant HTTPS vers
dev.azure.com(et téléchargement agent) ;sudo; Git, curl, jq - Familiarité de base avec un
azure-pipelines.yml(trigger, steps) - Budget : 0 € en local ; quelques cents/heure si VM Azure (deallocate après le lab)
Coût & minutes : les jobs self-hosted ne consomment en général pas les minutes Microsoft-hosted (vérifiez Billing). Le PAT d’enregistrement est un secret : durée courte, scopes minimaux, jamais commitné.
Maillage utile : Agents Microsoft-hosted · Azure DevOps Tools · Release pipeline · draft P4 agents self-hosted.
Ce que nous allons construire
Organisation Azure DevOps
└── Agent pool « linux-onprem » (ou ado-lab-self)
└── Agent « build-ubuntu-01 » (Linux, Online)
Machine lab (user azagent)
└── ~/myagent/
├── config.sh (URL org + pool + PAT au prompt)
├── svc.sh (unité systemd)
└── _work/ (workspaces des jobs)
YAML pipeline
└── pool: name: linux-onprem # sans vmImage
(Schéma maison à brancher à la publish : cover assets/web/devopelastichayway/cover-azure-pipelines-agents-self-hosted-1200x630.webp, alt : « Agent self-hosted Azure DevOps : pool, machine Linux, PAT d’enregistrement, YAML pool name ».)
Un agent self-hosted est une machine que vous contrôlez qui interroge Azure DevOps pour exécuter des jobs. Contrairement aux agents Microsoft-hosted (VM jetable, réseau public, caches vides), vous gagnez accès privé, caches persistants et outils custom — au prix du patching et de la confiance accordée à l’hôte.
Étape 1 — Microsoft-hosted vs self-hosted : quand choisir quoi
| Critère | Microsoft-hosted | Self-hosted |
|---|---|---|
| Setup | Aucun | Installer + maintenir agent et outils |
| Coût Free | 1 job parallèle (~1 800 min/mois), puis ~40 USD/mois/job | 1 job parallèle self-hosted puis ~15 USD/mois/job + votre VM |
| Réseau | Internet public seulement | On‑prem, VNet, registres privés, bases internes |
| Cache | VM propre à chaque run | Couches Docker, packages, repos → builds plus rapides |
| Matériel / logiciels | Images fixes | GPU, licences, OS spécifiques |
| Sécurité | Isolé, jetable | À votre charge : patch, isolation, rotation credentials |
Motifs valides : minutes hosted saturées, outils absents des images, accès réseau privé, cache disque, conformité. À éviter : contourner la sécurité, agent root exposé, PAT large partagé, code de forks non fiables sur votre réseau (préférez hosted pour les PR de forks).
Étape 2 — Préparer l’hôte Linux
L’agent (runtime .NET) exige ICU. Vos pipelines demanderont Git, Docker, Node, .NET, Java… Installez le socle et un compte non root dédié.
sudo apt update && sudo apt install -y curl git jq libicu74 ca-certificates
# Ubuntu 22.04 : libicu70 — RHEL 9 : dnf install -y libicu
sudo useradd -m -s /bin/bash azagent
# Optionnel Docker (builds d’images) :
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker azagent
Ouvrez uniquement le sortant 443 vers Azure DevOps. Traitez l’hôte comme de la prod : patches, monitoring, pas de sessions interactives quotidiennes.
Lab bonus — smoke ICU : si l’agent refuse de démarrer (« Couldn’t find a valid ICU package »), vérifiez libicu* ou testez temporairement DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 (lab uniquement, pas une excuse pour ignorer ICU en réel).
Étape 3 — Créer l’agent pool
- Organization settings → Pipelines → Agent pools → Add pool.
- Type Self-hosted, nom parlant :
linux-onprem(ouado-lab-selfen série P4). - En lab vous pouvez cocher l’accès large aux pipelines ; en entreprise, autorisez pipeline par pipeline.
- Ouvrez le pool → Security : votre compte en Administrator (requis pour enregistrer des agents).
- New agent → Linux x64 → notez le lien de téléchargement du jour.
Alternative : pool au niveau Project settings si votre org l’autorise.
Étape 4 — PAT d’enregistrement (placeholder seulement)
Le PAT sert surtout à l’enregistrement ; ensuite l’agent utilise son propre jeton OAuth. Créez un PAT dédié (avatar → Personal access tokens → New Token) :
| Champ | Valeur lab |
|---|---|
| Name | pat-agent-linux-onprem-YYYYMM |
| Expiration | Courte (7 jours) |
| Scopes | Agent Pools (Read & manage) uniquement |
Règles d’or : affiché une fois ; coffre / gestionnaire ; dans ce cours uniquement <PAT> ; jamais dans Git, Slack, README, captures ; révoquez après enregistrement ou en fin de lab ; jamais echo <PAT> dans un job.
# INTERDIT
PAT=xxxxxxxxxxxxxxxx
curl ... -H "Authorization: Basic <vrai-jeton>"
Étape 5 — Télécharger et configurer l’agent
Sous l’utilisateur azagent (téléchargez toujours depuis le portail / release officielle — la version change) :
sudo -iu azagent
mkdir -p ~/myagent && cd ~/myagent
# Lien exact : bouton New agent du pool (recommandé)
# Ou dernière release linux-x64 via API GitHub microsoft/azure-pipelines-agent :
AGENT_URL=$(curl -s https://api.github.com/repos/microsoft/azure-pipelines-agent/releases/latest
| jq -r '.assets[] | select(.name | test("vsts-agent-linux-x64-.*tar.gz$")) | .browser_download_url')
echo "$AGENT_URL"
curl -fsSL "$AGENT_URL" -o agent.tar.gz
tar zxf agent.tar.gz && rm agent.tar.gz
./config.sh
Réponses typiques : Server URL https://dev.azure.com/<org> · Auth PAT · jeton collé au prompt · Pool linux-onprem · Agent name build-ubuntu-01 · Work folder _work.
Mode non interactif (cloud-init / Ansible) — le secret vient d’un coffre, pas de l’historique shell :
./config.sh --unattended
--url "https://dev.azure.com/<your-organization>"
--auth pat --token "$AZP_TOKEN"
--pool linux-onprem
--agent "$(hostname)"
--work _work
--acceptTeeEula
--replace
Étape 6 — Service systemd et premier Online
./run.sh suffit pour un smoke test. Pour survivre aux reboots :
exit # revenir à un compte sudo
cd /home/azagent/myagent
sudo ./svc.sh install azagent
sudo ./svc.sh start
sudo ./svc.sh status
sudo journalctl -u 'vsts.agent.*' -f
Dans le pool : badge Online. Onglet Capabilities : Agent.OS, docker, git… Ajoutez des capacités utilisateur (gpu=true) et ciblez-les avec demands dans le YAML.
Lab bonus — diagnostic : lancez un job hello-world, puis vérifiez journalctl et l’espace disque de _work. Si Offline : DNS/proxy vers dev.azure.com ; ./config.sh --proxyurl derrière un proxy d’entreprise.
Étape 7 — Cibler le pool depuis YAML
# azure-pipelines.yml — self-hosted : PAS de vmImage
trigger:
branches:
include: [main]
pool:
name: linux-onprem
demands:
- docker # optionnel
variables:
imageName: myapp
steps:
- checkout: self
clean: true
- script: |
docker build -t $(imageName):$(Build.BuildId) .
docker image ls $(imageName)
displayName: Build container image
- script: |
echo "Running on $(Agent.Name) / $(Agent.OS)"
df -h $(Agent.WorkFolder)
displayName: Agent diagnostics
Premier run : Permit la pipeline sur le pool (autorisation one-shot). Pour une machine précise : demands: Agent.Name -equals build-ubuntu-01. Un agent = un job à la fois ; scalez avec plusieurs dossiers agents ou une VM/conteneur par agent.
Étape 8 — Exploitation, Docker éphémère et sécurité
Updates : ADO met souvent à jour l’agent automatiquement ; sinon Update all agents sur le pool. Disque : activez la maintenance du pool pour purger _work ; docker system prune si besoin. Retrait :
sudo ./svc.sh stop && sudo ./svc.sh uninstall
sudo -u azagent ./config.sh remove --auth pat --token "$AZP_TOKEN"
Alternative Docker / K8s : agent conteneur qui s’enregistre au start et se désinscrit (./run.sh --once) ; sur Kubernetes, scaler type KEDA azure-pipelines. Utile pour code non fiable — moins de « snowflake » qu’un agent longue durée.
Checklist sécurité (non négociable) :
- User non privilégié — jamais root pour tourner les jobs.
- PAT scope unique Agent Pools, expiration courte, révocation après enregistrement si possible.
- Pas d’accès « all pipelines » sur org partagée — approve par projet.
- Hôte patché, monitoring, firewall sortant 443 seulement.
- PR de forks → Microsoft-hosted uniquement.
- Pas de secrets longue durée sur le disque agent ; préférez Variable groups / Key Vault / OIDC.
- Ne partagez pas un même agent entre projets hostiles (fuite via
_work).
Scénario terrain : un stagiaire commit un PAT Full access « pour que ça marche ». Révoquez immédiatement, auditez les pools, régénérez un PAT Agent Pools minimal, et documentez l’incident dans votre runbook lab.
Lab bonus — cache, demands et maintenance
Après le hello-world, enchaînez ces micro-labs (toujours hors prod) :
- Demands : ajoutez une capacité utilisateur
lab=truesur l’agent (pool → agent → Capabilities) puis exigez-la dans le YAML. Confirmez qu’un agent sans la capacité laisse le job en file. - Cache disque : lancez deux builds Docker successifs sur le même hôte et comparez la durée ; documentez pourquoi le second est plus rapide (couches locales) et le risque associé (état partagé entre jobs).
- Maintenance : activez l’historique de maintenance du pool pour purger les anciens dossiers
_work, puis vérifiez l’espace disque avant/après. - Failover mental : simulez un agent Offline (
svc.sh stop) et observez la queue ; redémarrez et validez la reprise. Notez le runbook en trois lignes pour votre équipe.
Checklist anti-dérive self-hosted
- [ ] Pool nommé, permissions pipeline explicites (pas « all pipelines » en org partagée)
- [ ] Compte
azagentsans sudo quotidien ; Docker group seulement si nécessaire - [ ] PAT
<PAT>court, scope Agent Pools, révoqué ou daté de rotation - [ ] systemd installé et testé après reboot
- [ ] YAML sans
vmImagesur ce pool ; demands documentées - [ ] Forks / code non fiable exclus de cet hôte
- [ ] VM
canadacentraldeallocatée hors lab ;_worket images Docker nettoyés - [ ] Lien menu / slug
azure-devops-self-hosted-agentsaligné avec ce draft à la publish ()
Ces exercices consolidement le choix self-hosted sans remplacer les agents Microsoft-hosted : gardez les deux options dans votre boîte à outils Azure Pipelines, et documentez pourquoi chaque pipeline cible tel pool.
Étape 9 — Vérification de fin de parcours
- Pool
linux-onpremcréé ; agent Online au moins une fois. - Un run YAML affiche
Agent.Name=build-ubuntu-01(ou le nom choisi). - PAT : scopes minimaux, absent du Git et de ce fichier.
- Vous savez stopper/désinstaller et révoquer le jeton.
- Décision documentée : rester self-hosted ou revenir à
ubuntu-latestpour la suite CI/CD.
Nettoyage
svc.sh stopsi la machine dort ; révoquez / régénérez les PAT agent.- Pool vide : supprimez seulement s’il n’est plus référencé.
- VM Azure
canadacentral: deallocate pour couper le compute. - Ne laissez pas un YAML pointant vers un pool fantôme (queue infinie).
- Conteneurs : retirez images/volumes lab ; révoquez
AZP_TOKEN.
Erreurs fréquentes
| Symptôme | Cause | Correction |
|---|---|---|
| Couldn’t find a valid ICU package | libicu manquant |
Installer libicu74 / libicu70 |
| Access denied… Manage permissions for pool | Pas Admin du pool | Security du pool → Administrator |
| Agent Offline | Service stop, DNS, proxy | svc.sh start ; journalctl ; --proxyurl |
| Job waits forever for an available agent | Mauvais pool.name, demands, permissions |
Nom exact ; Permit pipeline ; demands |
vmImage + name self-hosted |
Confusion hosted/self | Self-hosted : name seul |
| PAT dans l’historique Git | Copier-coller | Révoquer + purge / nouveau repo lab |
| Disk full | _work / Docker |
Maintenance pool ; prune Docker |
Auth failed au config.sh |
PAT expiré / mauvais scope | Nouveau PAT Agent Pools au prompt |
Quiz (5 questions)
1. Pourquoi un PAT court avec scope Agent Pools seulement ?
– A. Azure Boards le facture à l’heure
– B. Limiter la surface si fuite ; l’enregistrement n’a pas besoin de Full access
– C. YAML refuse les scopes longs
2. Comment cibler un pool self-hosted ?
– A. pool: name: linux-onprem (sans vmImage)
– B. pool: vmImage: linux-onprem
– C. Uniquement via Azure Boards
3. Où doivent tourner les builds de PR provenant de forks non fiables ?
– A. Sur votre agent on‑prem root
– B. De préférence sur Microsoft-hosted
– C. Sur n’importe quel agent avec Docker.sock ouvert au monde
4. Que faire si l’agent reste Offline après reboot ?
– A. Supprimer l’organisation
– B. Vérifier le service systemd (svc.sh / journalctl) et la connectivité HTTPS
– C. Mettre le PAT en clair dans le README
5. Un agent self-hosted exécute combien de jobs en parallèle par défaut ?
– A. Illimité
– B. Un job à la fois
– C. Exactement 10
Réponses : 1‑B · 2‑A · 3‑B · 4‑B · 5‑B
Pour aller plus loin
- Doc officielle : Self-hosted Linux agents, Run a self-hosted agent in Docker, Agent pools, PAT scopes
- Suite menu : Release pipelines · Microsoft-hosted agents · CI/CD & outils Azure DevOps
Maillage série Azure DevOps
| ← Lié | Azure DevOps Microsoft-hosted agents |
| → Suite | Azure Release Pipeline / CI build |
| Aussi | Azure DevOps Tools · Sécurité & gouvernance · YAML pipelines |
Retour parcours Azure DevOps — hub de la série et leçons sœurs.



