On-demand self-hosted GitHub Actions runners on Aruba Cloud.
Each workflow gets a fresh, ephemeral cloud server. The server is created at the start of the job and deleted at the end โ no idle costs, no shared state between runs.
- A lightweight orchestration job (on a GitHub-hosted runner) calls this action in create mode.
- The action provisions an Aruba Cloud server via
acloud-cli, injects a cloud-init script, and waits for the GitHub runner agent to register. - Your actual job runs on the ephemeral runner (
runs-on: ${{ needs.start-runner.outputs.label }}). - A final job (with
if: always()) calls this action in delete mode to terminate the server.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Workflow โ
โ โ
โ start-runner โโโบ [this action: create] โ
โ โ โข provisions Aruba Cloud server โ
โ โ โข waits for runner registration โ
โ โผ โ
โ your-job โโโโโโโบ runs-on: ephemeral runner โ
โ โ โ
โ โผ โ
โ stop-runner โโโบ [this action: delete] (if: always()) โ
โ โข deletes the server โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- An Aruba Cloud account with API credentials (
client-idandclient-secret). - A project with a pre-created VPC, subnet, security group, and SSH key pair. Resource URIs for each are required inputs.
- A GitHub PAT with Read and write โ Administration permission on the target repository (or
admin:orgfor organisation runners).
name: CI on Aruba Cloud
on: [push]
jobs:
start-runner:
name: Start ephemeral runner
runs-on: ubuntu-latest
outputs:
label: ${{ steps.runner.outputs.label }}
server_id: ${{ steps.runner.outputs.server_id }}
project_id: ${{ steps.runner.outputs.project_id }}
boot_disk_id: ${{ steps.runner.outputs.boot_disk_id }}
steps:
- uses: Arubacloud/acloud-github-runner@v1
id: runner
with:
mode: create
github_token: ${{ secrets.GH_PAT }}
acloud_client_id: ${{ secrets.ACLOUD_CLIENT_ID }}
acloud_client_secret: ${{ secrets.ACLOUD_CLIENT_SECRET }}
acloud_project_id: ${{ secrets.ACLOUD_PROJECT_ID }}
vpc_uri: ${{ secrets.ACLOUD_VPC_URI }}
subnet_uri: ${{ secrets.ACLOUD_SUBNET_URI }}
security_group_uri: ${{ secrets.ACLOUD_SECURITY_GROUP_URI }}
keypair_uri: ${{ secrets.ACLOUD_KEYPAIR_URI }}
flavor: CSO2A4
image: LU22-001
build:
name: Build
needs: start-runner
runs-on: ${{ needs.start-runner.outputs.label }}
steps:
- uses: actions/checkout@v4
- run: echo "Running on Aruba Cloud!"
stop-runner:
name: Stop ephemeral runner
needs: [start-runner, build]
runs-on: ubuntu-latest
if: always()
steps:
- uses: Arubacloud/acloud-github-runner@v1
with:
mode: delete
github_token: ${{ secrets.GH_PAT }}
acloud_client_id: ${{ secrets.ACLOUD_CLIENT_ID }}
acloud_client_secret: ${{ secrets.ACLOUD_CLIENT_SECRET }}
acloud_project_id: ${{ needs.start-runner.outputs.project_id }}
server_id: ${{ needs.start-runner.outputs.server_id }}
boot_disk_id: ${{ needs.start-runner.outputs.boot_disk_id }}| Input | Required | Default | Description |
|---|---|---|---|
mode |
yes | โ | create or delete |
github_token |
yes | โ | PAT with Administration read/write |
acloud_client_id |
yes | โ | Aruba Cloud API client ID |
acloud_client_secret |
yes | โ | Aruba Cloud API client secret |
acloud_project_id |
yes | โ | Aruba Cloud project ID |
name |
no | acloud-runner-<run_id>-<attempt> |
Server name and runner label |
region |
no | ITBG-Bergamo |
Aruba Cloud region |
zone |
no | ITBG-1 |
Availability zone |
flavor |
no | CSO2A4 |
Server size (see Flavors) |
image |
no | LU22-001 |
Boot image used to create the boot disk (see Images) |
boot_disk_size |
no | 20 |
Boot disk size in GB |
boot_disk_type |
no | Performance |
Boot disk type (Performance or Archive) |
boot_disk_wait |
no | 30 |
Max polling attempts for boot disk NotUsed status (ร10 s) |
boot_disk_id |
yes (delete) | โ | Boot disk ID returned by the create step |
vpc_uri |
yes (create) | โ | VPC resource URI |
subnet_uri |
yes (create) | โ | Subnet resource URI |
security_group_uri |
yes (create) | โ | Security group resource URI |
keypair_uri |
yes (create) | โ | SSH key pair resource URI |
runner_labels |
no | self-hosted,linux,acloud |
Extra runner labels (comma-separated) |
runner_version |
no | latest |
GitHub Actions Runner version |
runner_dir |
no | /actions-runner |
Runner installation path on server |
pre_runner_script |
no | "" |
Bash commands to run before the runner starts |
runner_wait |
no | 60 |
Max polling attempts for runner registration (ร10 s) |
server_wait |
no | 30 |
Max polling attempts for server active status (ร10 s) |
server_id |
yes (delete) | โ | Server ID returned by the create step |
| Output | Description |
|---|---|
label |
Runner label โ use as the runs-on value in your job |
server_id |
Aruba Cloud server ID โ pass to the delete step together with project_id |
project_id |
Aruba Cloud project ID โ a server is uniquely identified by server_id + project_id; pass both to the delete step |
boot_disk_id |
ID of the boot disk created for the server โ pass to the delete step so it is removed together with the server |
Source: Aruba Cloud API metadata
| Flavor | vCPU | RAM |
|---|---|---|
| CSO1A2 | 1 | 2 GB |
| CSO1A4 | 1 | 4 GB |
| CSO2A4 | 2 | 4 GB |
| CSO2A8 | 2 | 8 GB |
| CSO4A8 | 4 | 8 GB |
| CSO4A16 | 4 | 16 GB |
| CSO8A16 | 8 | 16 GB |
| CSO8A32 | 8 | 32 GB |
| CSO16A32 | 16 | 32 GB |
| CSO16A64 | 16 | 64 GB |
| CSO32A64 | 32 | 64 GB |
Windows flavors start from CSO1A4. All Linux flavors โฅ CSO1A4 are also available for Windows images.
Source: Aruba Cloud API metadata
| Image code | Operating system |
|---|---|
LU20-001 |
Ubuntu 20.04 LTS (64-bit) |
LU22-001 |
Ubuntu 22.04 LTS (64-bit) |
LU24-001 |
Ubuntu 24.04 LTS (64-bit) |
DE11-001 |
Debian 11 (64-bit) |
DE12-001 |
Debian 12 (64-bit) |
alma8 |
AlmaLinux 8 (64-bit) |
alma9 |
AlmaLinux 9 (64-bit) |
osuse15_2_x64_1_0 |
openSUSE 15 (64-bit) |
WS19-001_W2K19_1_0 |
Windows Server 2019 |
WS22-001_W2K22_1_0 |
Windows Server 2022 |
| Region | Zones |
|---|---|
ITBG-Bergamo |
ITBG-1, ITBG-2, ITBG-3 |
Once a resource is created in a location it cannot be moved.
| Use case | Scope |
|---|---|
| Repository runner | repo |
| Organisation runner | admin:org |
Runner never registers
Cloud-init takes 2โ4 minutes to install packages and download the runner binary. Increase runner_wait (each unit = 10 s) if your image or network is slow. Check cloud-init logs on the server: /var/log/cloud-init-output.log.
Server not deleted after failure
The delete step uses if: always() so it runs even when earlier jobs fail. Verify the server_id output is correctly passed via needs.<start-job>.outputs.server_id. You can also delete servers manually from the Aruba Cloud portal.
acloud-cli authentication fails
Ensure ACLOUD_CLIENT_ID and ACLOUD_CLIENT_SECRET are stored as repository secrets and mapped to the action inputs. The credentials are stored in ~/.acloud.yaml with 0600 permissions during the run.
Apache 2.0 โ see LICENSE.