Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Create .env file using this file as a template.
# Define all your variables for local development as described below.
#
#
# - Use environment variables with prefix "AWS_" to give access to your test AWS account.
# - Use environment variables with prefix "INPUT_" to simulate GitHub Actions input.
# - Use GITHUB_REPOSITORY environment variable to provide the GitHub repository and it's owher as a context in the script. Use formst "owner/repo".
Expand All @@ -19,3 +19,8 @@ INPUT_SECURITY-GROUP-ID=
INPUT_LABEL=
INPUT_EC2-INSTANCE-ID=
GITHUB_REPOSITORY=
INPUT_EC2-VOLUME-SIZE=
INPUT_EC2-DEVICE-NAME=
INPUT_EC2-VOLUME-TYPE=
INPUT_USE-JIT=
INPUT_RUNNER-GROUP-ID=
2 changes: 1 addition & 1 deletion .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24

- name: Install packages
run: npm ci
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
.env
.env
.vscode
.claude
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Changelog

## [Unreleased]

### Added
- JIT (Just-In-Time) runner support via new `use-jit` input (default: `false`).
JIT runners use GitHub's `generate-jitconfig` API, skip `config.sh`,
and auto-deregister after completing one job.
- New `runner-group-id` input for specifying the runner group when using JIT mode (default: `1`).
- Validation: `use-jit` and `run-runner-as-service` cannot be used together (JIT is single-use).
- New `runner-debug` input (default: `false`) for verbose debug logging. When enabled,
injects detailed echo statements into the setup script and polls EC2 serial console
output during runner registration. Requires `ec2:GetConsoleOutput` IAM permission.
- New `availability-zones-config` input for multi-AZ failover. The action tries each
configuration in sequence until an instance is successfully launched.
- New `metadata-options` input for configuring EC2 instance metadata (e.g. IMDSv2).
- New `packages` input for installing packages via cloud-init during boot.
- New `region` output for tracking which AWS region the instance was launched in.
- EC2 console output polling via `GetConsoleOutputCommand` for remote debugging.
- Test suite expanded to 25 tests covering JIT, debug mode, cloud-boothook,
runuser, tolerant chown, stale config cleanup, and package installation.

### Changed
- Upgraded action runtime from `node20` to `node24` to resolve GitHub Actions deprecation warning (Node.js 20 actions deprecated June 2026).
- Updated `package.yml` workflow to build distribution with Node.js 24.
- Switched user-data format from `#cloud-config` with `runcmd` to `#cloud-boothook`.
This fixes compatibility with Amazon Linux 2023 and other AMIs where
`cloud_final_modules` may be empty or misconfigured.
- Replaced `su <user> -c` with `runuser -u <user> --` to avoid password prompts
in non-interactive cloud-init contexts.
- Made `chown` tolerant of permission errors (`|| true`) to prevent `set -e`
from killing the script when `_diag/` files are owned by root.
- Setup script now removes stale runner config files (`.runner`, `.credentials`,
`.credentials_rsaparams`) before `config.sh` to handle AMIs created from
previously configured runner instances.
- Setup script logs are written to `/tmp/runner-setup.log` instead of
`/var/log/user-data.log` and `/dev/console` (which may not be accessible).
- Updated README with full documentation for all new inputs, IAM requirements
for debug mode, and advanced usage sections (JIT, Multi-AZ, Debug).
200 changes: 170 additions & 30 deletions README.md

Large diffs are not rendered by default.

146 changes: 115 additions & 31 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: On-demand self-hosted AWS EC2 runners for GitHub Actions
description: GitHub Action for automatic creation and registration AWS EC2 instances as GitHub Actions self-hosted runners.
name: On-demand self-hosted AWS EC2 runner for GitHub Actions
description: GitHub Action for automatic creation and registration AWS EC2 instance as a GitHub Actions self-hosted runner.
author: Volodymyr Machula
branding:
icon: 'box'
Expand All @@ -8,8 +8,8 @@ inputs:
mode:
description: >-
Specify here which mode you want to use:
- 'start' - to start new runners;
- 'stop' - to stop the previously created runners.
- 'start' - to start a new runner;
- 'stop' - to stop the previously created runner.
required: true
github-token:
description: >-
Expand All @@ -18,12 +18,22 @@ inputs:
key-pair-name:
description: >-
Key pair name to use when creating the runner instances.
This input is required if you use the 'start' mode.
Optional, but useful when you need SSH access for debugging.
required: false
availability-zones-config:
description: >-
JSON string array of objects with configurations for multiple availability zones.
Each object should contain 'imageId', 'subnetId', and 'securityGroupId'.
Optionally, you can specify 'region' to launch the instance in a specific AWS region.
If 'region' is not specified, the default AWS_REGION environment variable will be used.
Example: '[{"imageId":"ami-123","subnetId":"subnet-123","securityGroupId":"sg-123"},{"imageId":"ami-456","subnetId":"subnet-456","securityGroupId":"sg-456","region":"us-west-2"}]'
When provided, the action will try each configuration in sequence until a successful instance is launched.
This takes precedence over individual ec2-image-id, subnet-id, and security-group-id parameters.
required: false
ec2-image-id:
description: >-
EC2 Image Id (AMI). The new runners will be launched from this image.
This input is required if you use the 'start' mode.
EC2 Image Id (AMI). The new runner will be launched from this image.
This input is required if you use the 'start' mode and don't provide availability-zones-config.
required: false
ec2-instance-type:
description: >-
Expand All @@ -32,44 +42,42 @@ inputs:
required: false
ec2-instance-count:
description: >-
Number of EC2 instances to create.
Number of EC2 instances to create. Defaults to 1.
Incompatible with 'use-jit: true'.
required: false
default: 1
default: '1'
subnet-id:
description: >-
VPC Subnet Id. The subnet should belong to the same VPC as the specified security group.
This input is required if you use the 'start' mode.
This input is required if you use the 'start' mode and don't provide availability-zones-config.
required: false
security-group-id:
description: >-
EC2 Security Group Id.
The security group should belong to the same VPC as the specified subnet.
The runners don't require any inbound traffic. However, outbound traffic should be allowed.
This input is required if you use the 'start' mode.
The runner doesn't require any inbound traffic. However, outbound traffic should be allowed.
This input is required if you use the 'start' mode and don't provide availability-zones-config.
required: false
label:
description: >-
Name of the unique label assigned to the runners.
The label is used to remove the runners from GitHub when the runners are not needed anymore.
Name of the unique label assigned to the runner.
The label is used to remove the runner from GitHub when the runner is not needed anymore.
This input is required if you use the 'stop' mode.
required: false
# This input's name is in the singular form for backwards compatibility
ec2-instance-id:
description: >-
EC2 Instance Ids of the created runners.
The ids are used to terminate the EC2 instances when the runners are not needed anymore.
This input is required if you use the 'stop' mode. The value can either be in the form of
a single raw string containing a single EC2 instance id, or a JSON-encoded string representing
an array of id strings.
EC2 Instance Id of the created runner.
The id is used to terminate the EC2 instance when the runner is not needed anymore.
This input is required if you use the 'stop' mode.
required: false
iam-role-name:
description: >-
IAM Role Name to attach to the created EC2 instances.
IAM Role Name to attach to the created EC2 instance.
This requires additional permissions on the AWS role used to launch instances.
required: false
aws-resource-tags:
description: >-
Tags to attach to the launched EC2 instances and volumes.
Tags to attach to the launched EC2 instance and volume.
This must be a stringified array of AWS Tag objects, with both Key and Value fields,
for example: '[{"Key": "TagKey1", "Value": "TagValue1"}, {"Key": "TagKey2", "Value": "TagValue2"}]'
required: false
Expand All @@ -89,22 +97,98 @@ inputs:
required: false
keep-runner-on-stop:
description: >-
If true, keep the runner alive (running) on stop, but still de-register it from GitHub. Useful for debugging failed jobs. Default false.
If true, keep the EC2 instance running on stop, but still de-register it from GitHub.
Useful for debugging failed jobs.
required: false
default: 'false'
block-device-mappings:
description: >-
JSON string specifying the block device mappings for the EC2 instance.
Example: '[{"DeviceName": "/dev/sda1", "Ebs": {"VolumeSize": 100, "VolumeType": "gp3"}}]'
required: false
startup-quiet-period-seconds:
description: >-
Specifies the quiet period in seconds after the instance starts.
The runner will not be registered during this period.
required: false
startup-retry-interval-seconds:
description: >-
Specifies the retry interval in seconds to register the runner after the quiet period.
required: false
startup-timeout-minutes:
description: >-
Specifies the timeout in minutes to register the runner after the quiet period.
required: false
default: '5'
run-runner-as-service:
description: >-
Start the runner as a service rather than using ./run.sh as root.
required: false
default: 'false'
run-runner-as-user:
description: >-
Specify user under whom the runner service should run
required: false
default: "false"

ec2-volume-size:
description: >-
EC2 volume size in GB.
required: false
ec2-device-name:
description: >-
EC2 block device name.
default: /dev/sda1
required: false
ec2-volume-type:
description: >-
EC2 block device type.
required: false
metadata-options:
description: >-
JSON string specifying the metadata options for the EC2 instance.
Example: '{"HttpTokens": "required", "HttpEndpoint": "enabled", "HttpPutResponseHopLimit": 2, "InstanceMetadataTags": "enabled"}'
packages:
description: >-
JSON array of packages to install via cloud-init.
Example: '["git", "docker.io", "nodejs"]'
required: false
default: '[]'
use-jit:
description: >-
Enable JIT (Just-In-Time) runner configuration. Uses GitHub's
generate-jitconfig API instead of the traditional registration-token approach.
JIT runners are single-use and auto-deregister after completing one job.
Incompatible with 'run-runner-as-service: true'.
required: false
default: 'false'
runner-group-id:
description: >-
The ID of the runner group to register the JIT runner in.
Defaults to 1, which is the "Default" runner group for repository-level runners.
Only used when 'use-jit' is true.
required: false
default: '1'
runner-debug:
description: >-
Enable verbose debug logging for the runner setup.
When true, outputs detailed instance info, console output polling,
and step-by-step script execution logs.
required: false
default: 'false'
outputs:
label:
description: >-
Name of the unique label assigned to the runners.
Name of the unique label assigned to the runner.
The label is used in two cases:
- to use as the input of 'runs-on' property for the following jobs;
- to remove the runners from GitHub when they are not needed anymore.
# This output's name is in the singular form for backwards compatibility
- to remove the runner from GitHub when it is not needed anymore.
ec2-instance-id:
description: >-
EC2 Instance Ids of the created runners.
The ids are used to terminate the EC2 instances when the runners are not needed anymore.
EC2 Instance Id of the created runner, or a JSON array of instance ids when multiple runners are launched.
The id or ids are used to terminate the EC2 instances when the runners are not needed anymore.
region:
description: >-
AWS region where the EC2 instance was created.
This is useful for subsequent AWS operations on the instance.
runs:
using: node20
using: node24
main: ./dist/index.js
Loading
Loading