Self-Hosted Runner Setup¶
Load-testing pipelines (fio, iPerf3, HammerDB, stress-ng) require a self-hosted runner on the cluster management network — they cannot use GitHub-hosted runners because they need direct SSH and WinRM access to cluster nodes.
Requirements¶
| Requirement | Details |
|---|---|
| OS | Windows Server 2022 or Windows 11 (22H2+) |
| PowerShell | 7.3 or later (winget install Microsoft.PowerShell) |
| .NET | .NET 8 SDK (GitHub Actions runner dependency) |
| OpenSSH client | Built-in on Windows Server 2022; verify with ssh -V |
| Network | Line-of-sight to cluster management IP range |
| WinRM | Must be able to reach cluster nodes on TCP 5985/5986 |
| GitHub Actions runner | v2.311.0 or later |
Installation Steps¶
1. Register the Runner¶
In the GitHub repository, go to Settings → Actions → Runners → New self-hosted runner. Follow the Windows instructions to download and configure the runner:
# Example — replace token and URL from the GitHub UI
mkdir "C:\actions-runner"; cd "C:\actions-runner"
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-win-x64-2.311.0.zip `
-OutFile actions-runner-win-x64.zip
Expand-Archive -Path actions-runner-win-x64.zip -DestinationPath .
.\config.cmd --url https://github.com/YOUR-ORG/azurelocal-loadtools `
--token YOUR_TOKEN `
--labels "self-hosted,windows,hci-management" `
--runnergroup Default `
--name "hci-mgmt-runner-01" `
--work "_work"
2. Install as a Windows Service¶
The service runs as NT AUTHORITY\NetworkService by default. For WinRM access to cluster nodes, you may need to change the service account to a domain user with appropriate permissions.
3. Install Required PowerShell Modules¶
# Run in an elevated PowerShell 7 session on the runner
$modules = @(
"Pester",
"PSScriptAnalyzer",
"powershell-yaml",
"Az.KeyVault",
"Az.Accounts"
)
foreach ($module in $modules) {
Install-Module -Name $module -Force -AllowClobber -Scope AllUsers
Write-Host "Installed: $module $(Get-Module $module -ListAvailable | Select-Object -First 1 -ExpandProperty Version)"
}
4. Configure SSH Key¶
The runner needs the SSH private key to connect to Linux cluster nodes:
# Create SSH directory and restrict permissions
New-Item -Path "$env:ProgramData\azurelocal-runner\.ssh" -ItemType Directory -Force
# Copy the private key (distribute from your secrets manager or GitHub Secrets via pipeline)
# Example: write key from environment variable
$env:LINUX_SSH_KEY | Set-Content "$env:ProgramData\azurelocal-runner\.ssh\azurelocal_rsa" -NoNewline
icacls "$env:ProgramData\azurelocal-runner\.ssh\azurelocal_rsa" /inheritance:r /grant:r "$($env:USERNAME):F"
In config/variables.yml, set:
5. Validate WinRM Connectivity¶
$clusterNodes = @("hci01-node1", "hci01-node2", "hci01-node3", "hci01-node4")
$cred = Get-Credential # or pull from Key Vault
foreach ($node in $clusterNodes) {
try {
$result = Invoke-Command -ComputerName $node -Credential $cred `
-ScriptBlock { hostname } -ErrorAction Stop
Write-Host "WinRM OK: $node → $result"
} catch {
Write-Warning "WinRM FAILED: $node — $($_.Exception.Message)"
}
}
6. Validate SSH Connectivity¶
$linuxNodes = @("hci01-node1", "hci01-node2")
$sshUser = "azurelocaladmin"
$keyPath = "$env:ProgramData\azurelocal-runner\.ssh\azurelocal_rsa"
foreach ($node in $linuxNodes) {
$result = ssh -i $keyPath -o BatchMode=yes -o ConnectTimeout=5 `
"$sshUser@$node" "echo ok" 2>&1
Write-Host "SSH $node: $result"
}
Runner Labels¶
Pipelines target the runner using the label hci-management. Verify that all pipeline YAML files reference this label:
# .github/workflows/run-fio.yml (example)
jobs:
run-fio:
runs-on: [self-hosted, windows, hci-management]
Maintenance¶
| Task | Frequency | Command |
|---|---|---|
| Update runner binary | Monthly | .\svc.cmd stop; .\config.cmd remove; # re-download and re-configure |
| Update PowerShell modules | Monthly | Update-Module -Scope AllUsers |
| Rotate SSH key | Per org policy | Re-run Step 4 and update authorized_keys on all nodes |
| Check runner status | On pipeline failures | Get-Service -Name "actions.runner.*" | Select-Object Status |