GitHub Action for creating and setting up a self-hosted runner on Aliyun ECS Spot instances.
- Dynamic selection of optimal Spot instances
- Support for specifying exact instance type (bypasses CPU/memory/arch selection)
- Automatic ECS Spot instance creation
- Automatic installation and configuration of GitHub Actions Runner
- Support for Ephemeral Runner mode (automatic cleanup after task completion)
- Support for instance self-destruct mechanism (automatic instance deletion after Runner exits)
- Support for AMD64 and ARM64 architectures
- Support for proxy configuration
- Automatic instance cleanup on failure
name: Build with Spot Runner
on:
workflow_dispatch:
jobs:
setup:
name: Setup Spot Instance
runs-on: ubuntu-latest
outputs:
instance_id: ${{ steps.setup-runner.outputs.instance_id }}
runner_name: ${{ steps.setup-runner.outputs.runner_name }}
runner_online: ${{ steps.setup-runner.outputs.runner_online }}
cpu_cores: ${{ steps.setup-runner.outputs.cpu_cores }}
steps:
- name: Setup Aliyun ECS Spot Runner
id: setup-runner
uses: dianplus/cloud-instance-github-runner@master
with:
aliyun_access_key_id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
aliyun_access_key_secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
aliyun_region_id: "cn-hangzhou"
aliyun_vpc_id: ${{ vars.ALIYUN_VPC_ID }}
aliyun_security_group_id: ${{ vars.ALIYUN_SECURITY_GROUP_ID }}
aliyun_image_family: "acs:ubuntu_24_04_arm64"
aliyun_ecs_self_destruct_role_name: "GitHubRunnerSelfDestructRole"
aliyun_key_pair_name: ${{ secrets.ALIYUN_KEY_PAIR_NAME }}
github_token: ${{ secrets.RUNNER_REGISTRATION_PAT }}
arch: arm64
min_cpu: 8
vswitch_id_b: ${{ vars.ALIYUN_VSWITCH_ID_B }}
vswitch_id_g: ${{ vars.ALIYUN_VSWITCH_ID_G }}
vswitch_id_h: ${{ vars.ALIYUN_VSWITCH_ID_H }}
vswitch_id_i: ${{ vars.ALIYUN_VSWITCH_ID_I }}
vswitch_id_j: ${{ vars.ALIYUN_VSWITCH_ID_J }}
vswitch_id_k: ${{ vars.ALIYUN_VSWITCH_ID_K }}
http_proxy: ${{ vars.HTTP_PROXY }}
https_proxy: ${{ vars.HTTPS_PROXY }}
no_proxy: ${{ vars.NO_PROXY }}
build:
name: Build
needs: setup
runs-on: [self-hosted, Linux, aliyun, spot-instance]
if: needs.setup.outputs.runner_online == 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
echo "Building on runner: ${{ needs.setup.outputs.runner_name }}"
echo "CPU cores: ${{ needs.setup.outputs.cpu_cores }}"When creating runners for different architectures in the same workflow, you can use architecture-specific image families:
name: Build with Multi-Arch Spot Runners
on:
workflow_dispatch:
jobs:
setup-amd64:
name: Setup AMD64 Spot Instance
runs-on: ubuntu-latest
# permissions configuration is optional since this Action uses PAT instead of GITHUB_TOKEN
steps:
- name: Setup Aliyun ECS Spot Runner
uses: dianplus/cloud-instance-github-runner@master
with:
aliyun_access_key_id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
aliyun_access_key_secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
aliyun_region_id: ${{ vars.ALIYUN_REGION_ID }}
aliyun_vpc_id: ${{ vars.ALIYUN_VPC_ID }}
aliyun_security_group_id: ${{ vars.ALIYUN_SECURITY_GROUP_ID }}
aliyun_image_family: acs:ubuntu_24_04_x64
github_token: ${{ secrets.RUNNER_REGISTRATION_PAT }}
arch: amd64
vswitch_id_b: ${{ vars.ALIYUN_VSWITCH_ID_B }}
setup-arm64:
name: Setup ARM64 Spot Instance
runs-on: ubuntu-latest
# permissions configuration is optional since this Action uses PAT instead of GITHUB_TOKEN
steps:
- name: Setup Aliyun ECS Spot Runner
uses: dianplus/cloud-instance-github-runner@master
with:
aliyun_access_key_id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
aliyun_access_key_secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
aliyun_region_id: ${{ vars.ALIYUN_REGION_ID }}
aliyun_vpc_id: ${{ vars.ALIYUN_VPC_ID }}
aliyun_security_group_id: ${{ vars.ALIYUN_SECURITY_GROUP_ID }}
aliyun_image_family: acs:ubuntu_24_04_arm64
github_token: ${{ secrets.RUNNER_REGISTRATION_PAT }}
arch: arm64
vswitch_id_b: ${{ vars.ALIYUN_VSWITCH_ID_B }}Note: Ensure aliyun_image_family matches arch (AMD64: acs:ubuntu_24_04_x64, ARM64: acs:ubuntu_24_04_arm64).
When you need to use a specific instance type, you can use the aliyun_instance_type parameter:
name: Build with Specific Instance Type
on:
workflow_dispatch:
jobs:
setup:
name: Setup Spot Instance with Specific Type
runs-on: ubuntu-latest
steps:
- name: Setup Aliyun ECS Spot Runner
uses: dianplus/cloud-instance-github-runner@master
with:
aliyun_access_key_id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
aliyun_access_key_secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
aliyun_region_id: "cn-hangzhou"
aliyun_vpc_id: ${{ vars.ALIYUN_VPC_ID }}
aliyun_security_group_id: ${{ vars.ALIYUN_SECURITY_GROUP_ID }}
aliyun_image_family: "acs:ubuntu_24_04_x64"
github_token: ${{ secrets.RUNNER_REGISTRATION_PAT }}
aliyun_instance_type: "ecs.c7.2xlarge"
vswitch_id_b: ${{ vars.ALIYUN_VSWITCH_ID_B }}
vswitch_id_g: ${{ vars.ALIYUN_VSWITCH_ID_G }}Note: When aliyun_instance_type is provided, the min_cpu, max_cpu, min_mem, max_mem, and arch parameters are ignored. The action will query spot prices for the specified instance type across all availability zones.
| Parameter | Description | Example |
|---|---|---|
aliyun_access_key_id |
Aliyun Access Key ID | LTAI5t... |
aliyun_access_key_secret |
Aliyun Access Key Secret | xxx... |
aliyun_region_id |
Aliyun Region ID | cn-hangzhou |
aliyun_vpc_id |
VPC ID | vpc-xxx |
aliyun_security_group_id |
Security Group ID | sg-xxx |
aliyun_image_id |
Image ID (optional if aliyun_image_family is provided) |
m-xxx |
github_token |
GitHub Token (for getting registration token) | ${{ secrets.RUNNER_REGISTRATION_PAT }} |
Important: github_token must be a PAT with appropriate permissions. See "Permission Configuration" below.
| Parameter | Description | Default |
|---|---|---|
aliyun_image_family |
Image Family (takes precedence over aliyun_image_id). Must match the architecture specified in arch parameter. For example, use acs:ubuntu_24_04_x64 for amd64, acs:ubuntu_24_04_arm64 for arm64. See Aliyun public image documentation for available image families. |
- |
aliyun_key_pair_name |
SSH Key Pair Name | - |
aliyun_ecs_self_destruct_role_name |
Instance Self-Destruct Role Name | - |
runner_labels |
Runner labels (comma-separated) | self-hosted,Linux,aliyun,spot-instance |
runner_version |
GitHub Actions Runner version | 2.311.0 |
aliyun_instance_type |
Specific instance type (e.g., ecs.c7.2xlarge). When provided, ignores min_cpu/max_cpu/min_mem/max_mem/arch and queries spot price for this exact type. Only single value allowed. |
- |
arch |
Architecture (amd64 or arm64) |
amd64 |
min_cpu |
Minimum CPU cores | 8 |
min_mem |
Minimum memory in GB (auto-calculated if not provided) | - |
max_cpu |
Maximum CPU cores | 64 |
max_mem |
Maximum memory in GB (auto-calculated if not provided) | - |
http_proxy |
HTTP Proxy URL | - |
https_proxy |
HTTPS Proxy URL | - |
no_proxy |
NO_PROXY environment variable value | - |
vswitch_id_a to vswitch_id_z |
VSwitch IDs for each availability zone | - |
Proxy Configuration (Mainland China Regions): Strongly recommended when using ECS instances in Mainland China regions.
| Parameter | Description |
|---|---|
instance_id |
Created ECS instance ID |
runner_name |
Runner name |
runner_online |
Whether runner is online (true/false) |
cpu_cores |
Instance CPU cores |
Recommended to create a separate VPC for CI/CD.
# Create VPC
aliyun vpc CreateVpc \
--RegionId ${ALIYUN_REGION_ID} \
--VpcName vpc-ci-runner \
--CidrBlock 172.16.0.0/16
# Create VSwitches in multiple availability zones
aliyun vpc CreateVSwitch \
--RegionId ${ALIYUN_REGION_ID} \
--VSwitchName vsw-ci-runner-b \
--VpcId vpc-xxx \
--CidrBlock 172.16.1.0/24 \
--ZoneId ${ALIYUN_REGION_ID}-baliyun ecs CreateSecurityGroup \
--RegionId ${ALIYUN_REGION_ID} \
--GroupName sg-ci-runner \
--VpcId vpc-xxx \
--Description "Security group for CI runners"Add the following Secrets in repository settings:
ALIYUN_ACCESS_KEY_ID: Aliyun Access Key IDALIYUN_ACCESS_KEY_SECRET: Aliyun Access Key Secret
Add the following Variables in repository settings:
ALIYUN_REGION_ID: Region ID (e.g.,cn-hangzhou)ALIYUN_VPC_ID: VPC IDALIYUN_SECURITY_GROUP_ID: Security Group IDALIYUN_VSWITCH_ID_AtoALIYUN_VSWITCH_ID_Z: VSwitch IDs for each availability zone (configure as needed)ALIYUN_AMD64_IMAGE_ID: AMD64 Image ID (recommended: Ubuntu 24)ALIYUN_ARM64_IMAGE_ID: ARM64 Image ID (recommended: Ubuntu 24)ALIYUN_KEY_PAIR_NAME: SSH Key Pair Name (optional)ALIYUN_ECS_SELF_DESTRUCT_ROLE_NAME: Instance Self-Destruct Role Name (optional)
This Action uses PAT to generate registration token, not GITHUB_TOKEN. Workflow permissions configuration does not apply.
PAT permission requirements:
- Classic PAT:
reposcope - Fine-grained PAT or GitHub Apps:
- Access: Only repositories where this action will run
- Permissions: Administration: Read and write (repository-level, not organization-level)
- Set a reasonable expiration time
RAM user needs the following permissions:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:RunInstances",
"ecs:DescribeInstances",
"ecs:DescribeImages",
"ecs:DescribeSecurityGroups",
"ecs:DescribeAvailableResource",
"ecs:DescribeSpotPriceHistory"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"vpc:DescribeVSwitches",
"vpc:DescribeVpcs"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ram:PassRole",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecs:DeleteInstance",
"ecs:DeleteInstances"
],
"Resource": "acs:ecs:*:*:instance/*",
"Condition": {
"StringEquals": {
"acs:ResourceTag/GITHUB_RUNNER_TYPE": [
"aliyun-ecs-spot"
]
}
}
}
]
}Create an ECS instance role with instance deletion permissions to enable self-destruct.
Create RAM Role:
aliyun ram CreateRole \
--RoleName GitHubRunnerSelfDestructRole \
--AssumeRolePolicyDocument '{
"Statement": [{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {"Service": ["ecs.aliyuncs.com"]}
}],
"Version": "1"
}'Create and attach policy:
aliyun ram CreatePolicy \
--PolicyName EcsSelfDestructPolicy \
--PolicyDocument '{
"Version": "1",
"Statement": [{
"Effect": "Allow",
"Action": ["ecs:DeleteInstance", "ecs:DescribeInstances"],
"Resource": "*",
"Condition": {"StringEquals": {"acs:ResourceTag/GITHUB_RUNNER_TYPE": ["aliyun-ecs-spot"]}}
}]
}'
aliyun ram AttachPolicyToRole \
--PolicyType Custom \
--PolicyName EcsSelfDestructPolicy \
--RoleName GitHubRunnerSelfDestructRoleConfigure GitHub Variable:
ALIYUN_ECS_SELF_DESTRUCT_ROLE_NAME: Role name (e.g.,GitHubRunnerSelfDestructRole)
If you also want to create custom images to shorten CI time, you need the following additional permissions:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:CreateImage",
"ecs:DescribeImages",
"ecs:ModifyImageAttribute",
"ecs:DeleteImage",
"ecs:DescribeImageFromFamily"
],
"Resource": "*"
}
]
}- CPU:RAM = 1:1 ratio
- Default range: 8c8g to 64c64g
- No instance family restrictions
- CPU:RAM = 1:2 ratio
- Default range: 8c16g to 64c128g
- Actually limited to
ecs.c8yandecs.c8rinstance families
- Use
spot-instance-advisorto select optimal Spot instance (or query spot price for a specific instance type ifaliyun_instance_typeis provided) - Create ECS Spot instance with User Data script
- Instance automatically installs and configures GitHub Actions Runner on startup
- Wait for Runner to register and come online
- Automatically delete instance after task completion
- Check if instance was created successfully
- View instance logs:
/var/log/user-data.log - Check Runner service status:
systemctl status actions.runner.*.service
- Check if VSwitch IDs are correctly configured
- Check if security group rules allow necessary traffic
- Check if image ID is valid
- View specific error messages in error logs
- Check if instance role is correctly configured
- View self-destruct logs:
/var/log/self-destruct.log - Check if post-job hook is executing normally
MIT