Sign inSign up

jnitecki/azp-agent

By jnitecki

•Updated 4 days ago

Self-hosted Azure Pipelines / Azure DevOps agent, Linux & Windows, optional Java/Android/.NET/Node

Image
Developer tools
0

1.2K

jnitecki/azp-agent repository overview

⁠azp-agent

Containerized self-hosted agent for Azure Pipelines / Azure DevOps, with optional build essentials and Python dev headers (Linux), Java, Android SDK (with emulator on Linux), PowerShell Core, .NET SDK, and Node.js toolchains.

VariantImage tag
Linuxdocker.io/jnitecki/azp-agent:latest
Windowsdocker.io/jnitecki/azp-agent:windows-latest

⁠Run

Both variants expect the standard Azure Pipelines agent environment variables:

VariableMeaning
AZP_URLAzure DevOps organization URL, e.g. https://dev.azure.com/<org>.
AZP_TOKENPersonal access token with Agent Pools (read, manage) scope.
AZP_POOLAgent pool name to register into (defaults to Default).
AZP_AGENT_NAMEOptional; defaults to the container's own hostname if unset.
AZP_WORKOptional working directory (defaults to _work).
AZP_RUN_ONCEOptional; true (default) runs a single job, then the agent deregisters and the container exits. false keeps the agent registered and taking jobs until the container is stopped. Also accepts 1/0 and yes/no.

With the default AZP_RUN_ONCE=true, run the container with a restart policy (e.g. --restart unless-stopped, as run.ps1 does) so a fresh agent registers after every job. Note that a restarted container keeps its filesystem, so to start each job from a clean environment, run a new container (e.g. --rm plus an external supervisor) instead of restarting the old one.

docker run -e AZP_URL=https://dev.azure.com/<org> \
           -e AZP_TOKEN=<pat> \
           -e AZP_POOL=<pool> \
           docker.io/jnitecki/azp-agent:latest
docker run -e AZP_URL=https://dev.azure.com/<org> `
           -e AZP_TOKEN=<pat> `
           -e AZP_POOL=<pool> `
           docker.io/jnitecki/azp-agent:windows-latest
⁠Toolchain capabilities (Linux)

The Linux agent reports these variables as capabilities, so pipelines can demand them:

VariableSet whenValue
GCCgcc (build-essential) is installedFull gcc version, e.g. 13.3.0
GCC_<major>gcc (build-essential) is installedPath to gcc, e.g. GCC_13=/usr/bin/gcc
PYTHON_DEVPython development headers (python3-dev) are installedPython version the headers are for, e.g. 3.12
ANDROID_EMULATORThe Android emulator is installed and emulator -accel-check succeeds at container startPath to the emulator, e.g. /opt/android-sdk/emulator/emulator
ANDROID_EMULATOR_<api>As ANDROID_EMULATOR, one per installed emulator system imagePath to the emulator, e.g. ANDROID_EMULATOR_36=/opt/android-sdk/emulator/emulator
PODMANPodman is installed and a test container started at container start runs successfullyPath to podman, e.g. /usr/bin/podman
pool:
  name: <pool>
  demands:
  - GCC_13
  - PYTHON_DEV -equals 3.12

The emulator is only included in the amd64 image (Google publishes no Linux arm64 emulator). It needs hardware acceleration, so the container must be given access to KVM (e.g. --device /dev/kvm, or --privileged as run.ps1 does) on a host that supports it. Without it, the emulator stays installed but ANDROID_EMULATOR* are not reported, so jobs that demand them won't be routed to this agent.

Nested Podman needs the container to run --privileged (as run.ps1 does). When running the container with Podman, also give nested Podman's storage its own volume, e.g. -v /var/lib/containers (as run.ps1 does when it runs the container with Podman and the image includes Podman). The volume then sits on the host's normal filesystem (e.g. WSL2's ext4 disk) instead of on the container's own overlay, so the nested fuse-overlayfs works on top of a regular filesystem. The official quay.io/podman/stable image is set up the same way.

At start, before registering, the agent checks that nested Podman actually works by running a small test container. The test needs no registry or network access: it builds a throwaway local image from the agent's own shell binaries, runs it with --cgroups=disabled --network=none and a read-only bind mount, creates a directory inside it, and removes the image again. If the test fails, the agent logs a warning with the error and removes PODMAN, so jobs that demand it are not routed to this agent. If Podman's image storage is on an overlay or FUSE filesystem, the warning also suggests mounting a volume at /var/lib/containers. The test adds some time to each container start (each job with the default AZP_RUN_ONCE=true) and gives up after 2 minutes per step.

If you have this repository cloned, run.ps1 scripts the above: it pulls the current image, stops/removes any previous azp-agent/azp-agent-NN container(s) together with their anonymous volumes, and starts fresh one(s). -AzpUrl/-AzpToken/-AzpPool are optional — if omitted, each falls back to the matching AZP_URL/AZP_TOKEN/AZP_POOL environment variable, then to a hardcoded default at the top of the script (empty by default; fill in locally, never commit real values), and the script fails fast if a value is still missing. -RunOnce true|false sets AZP_RUN_ONCE the same way (parameter, then environment variable), but if neither is given the image default applies.

# Single Linux agent named after this host, using the latest image
./run.ps1 -AzpUrl https://dev.azure.com/<org> -AzpToken <pat> -AzpPool <pool>

# Three Linux agents: azp-agent-01..03, named <hostname>-01..03
./run.ps1 -AzpUrl https://dev.azure.com/<org> -AzpToken <pat> -AzpPool <pool> -InstanceCount 3

# Persistent agent that keeps taking jobs instead of exiting after one
./run.ps1 -AzpUrl https://dev.azure.com/<org> -AzpToken <pat> -AzpPool <pool> -RunOnce false

# Windows variant, explicit version
./run.ps1 -AzpUrl https://dev.azure.com/<org> -AzpToken <pat> -AzpPool <pool> -Os Windows -Version 4.248.0

Tag summary

Content type

Image

Digest

sha256:1a1d1dd3d…

Size

1.3 GB

Last updated

4 days ago

docker pull jnitecki/azp-agent