machulav/ec2-github-runner: an EC2 runner that exists only for the length of a job
On-demand self-hosted AWS EC2 runner for GitHub Actions
At a glance
- What is it?
- A GitHub Action that starts an EC2 instance, registers it as a self-hosted runner, runs one job, and terminates the instance. It is aimed at teams that need VPC access, non-standard hardware, or cheaper compute than GitHub-hosted runners, and it costs you an AWS account, an IAM policy, and a personal access token to operate.
- Who is it for?
- Adopt it if your workflow needs to reach resources inside a VPC, needs an instance type GitHub does not offer, or runs long enough that a smaller EC2 instance beats the per-minute price of a hosted runner, and if you can own the IAM policy and the repo-scoped personal access token it requires.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 130 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap the action fills: GitHub-hosted runners are one fixed machine in someone else's network
GitHub's Linux virtual machines come in a single size, which the README describes as 2-core CPU, 7 GB of RAM and 14 GB of SSD disk space. They also run outside your AWS account, so a workflow step cannot reach a database sitting in a private subnet of your VPC. Those are two different problems with one shared shape: the machine running your job is not the machine you want. The third problem is price. The README points out that self-hosted runners are free to use with GitHub Actions and that you pay only for the runner machines, so a job that runs for more than a couple of minutes on modest hardware can be cheaper on a small EC2 instance than on a hosted runner. This action is for teams that hit at least one of those three walls and are willing to run the compute themselves. It is not a general CI replacement. It is a provisioning step you place at the front of a workflow.
Start, register, run, terminate: the lifecycle the action owns
The README states the sequence plainly: start your EC2 self-hosted runner right before you need it, run the job on it, stop it when you finish, and do all of that automatically as part of the workflow. The action therefore has to do more than call RunInstances. It creates the instance in a subnet you name, registers that instance as a self-hosted runner against your GitHub account at the repository level, waits for the runner to come up, and then removes both the runner registration and the instance when the job ends. The image in the README (docs/images/github-actions-summary.png) shows the resulting job summary inside a workflow run. Two advanced modes are documented. JIT runners use GitHub's just-in-time runner registration, which is the newer path for handing a runner its credentials. Multi-AZ failover lets the action try more than one availability zone, which matters when a single zone has no capacity for the instance type you asked for. The action is written in JavaScript and ships as a GitHub Action, so the mechanism lives in the action's own code rather than in a CLI you install.
The IAM policy is the real configuration surface
Most of the setup work is not in the workflow YAML. It is in the IAM user that the action assumes. The README gives a least-privilege baseline of four EC2 actions: RunInstances, TerminateInstances, DescribeInstances and DescribeInstanceStatus. Each optional feature widens that policy. Enabling the runner-debug input, which polls the EC2 serial console during startup, adds ec2:GetConsoleOutput. Attaching an IAM role to the runner through the iam-role-name parameter adds ec2:ReplaceIamInstanceProfileAssociation and ec2:AssociateIamInstanceProfile, plus iam:PassRole. Using the aws-resource-tags parameter adds ec2:CreateTags, scoped in the README's example by a condition on ec2:CreateAction equal to RunInstances. The README is explicit that these policies are a guide and can and most likely should be narrowed further by naming specific resources. That is the honest framing: the defaults are a starting point, not a hardened posture. Alongside the AWS keys, the action needs a GitHub personal access token with the repo scope, because it manages self-hosted runners at the repository level. Credentials are wired in through aws-actions/configure-aws-credentials, per the README's step 3.
Spot instances and the service-linked role trap
If you plan to request Spot capacity, the README carries an important warning. AWS provisions Spot instances through a service-linked role, and that role must already exist or be creatable at runtime. The README lists three ways to satisfy this: the role already exists because you once requested a Spot instance through the AWS Console, you create the role yourself through the Console, AWS CLI or API, or you grant the IAM role used by the action permission to create the service-linked role at runtime. A workflow that works fine on On-Demand instances can fail on its first Spot attempt for a reason that has nothing to do with the action's inputs. This is the kind of failure that shows up as an opaque AWS error inside a CI log. If Spot pricing is part of your cost case, resolve the service-linked role before you write the workflow, not after the first red build.
Where it is the wrong tool, starting with public repositories
The README devotes a section to self-hosted runner security with public repositories, and the placement is a signal. A runner that a fork's pull request can execute on is a runner that untrusted code can execute on, inside your VPC, with whatever IAM role you attached. The action's own use case of reaching private resources in your VPC makes that worse rather than better, because the runner is deliberately placed where your internal services live. Beyond that, every job pays a cold start: instance launch, runner registration, and the wait for the runner to become available. A job that takes ninety seconds of real work can spend longer than that waiting for the machine. There is also no pool here. Each workflow run provisions its own instance, so ten concurrent jobs mean ten instances and ten sets of AWS API calls, subject to whatever EC2 limits your account carries. Teams that want warm, persistent runners with a scheduler in front of them are solving a different problem and should not reach for this action.
Compared with keeping a runner host running
The obvious alternative is a long-lived EC2 instance registered once as a self-hosted runner, either permanently or in an autoscaling group. That approach removes the cold start entirely: the machine is already registered and already warm when the job arrives. The difference in approach is who manages the lifecycle. A permanent runner host is your responsibility to patch, monitor and eventually replace, and it sits idle between jobs while still billing. The action inverts that: it accepts a per-job provisioning delay in exchange for there being no machine to maintain between jobs and no idle compute cost. The trade is cold start and API dependency against standing infrastructure and patching work. If your jobs are frequent and short, the permanent host usually wins on wall-clock time. If your jobs are occasional, long, or need instance types you would not want to pay for around the clock, the on-demand model is the better fit. Note that the README's cost argument rests on jobs that take more than a couple of minutes, which is the same boundary in the other direction.
Maintenance, releases and what the MIT licence leaves to you
The repository is not archived and the last push recorded is 2026-05-08. Releases are sparse and uneven: v2 dates from June 2021, then nothing until v2.6.0 and v2.6.1 in April 2026. That gap is worth understanding before you depend on the action, because it means the v2 line sat unchanged for years and then resumed. Pin to a tag rather than to a branch if you want the workflow to behave the same next month. The licence is MIT, which is permissive and places few obligations on how you use or redistribute the code; it does not, however, transfer any responsibility for the AWS resources the action creates, the IAM policy you grant, or the personal access token you store. Those remain yours. The README also notes that the example policies should most likely be narrowed to specific resources, which is an ongoing task rather than a one-time setup step. Budget for reviewing that policy whenever you enable a new input.
Editorial conclusion
Adopt it if your workflow needs to reach resources inside a VPC, needs an instance type GitHub does not offer, or runs long enough that a smaller EC2 instance beats the per-minute price of a hosted runner, and if you can own the IAM policy and the repo-scoped personal access token it requires. Do not adopt it for public repositories, where the README's own security section says self-hosted runners should not be used, or if you want a pool of always-warm machines with no cold start. Before wiring it into a workflow, confirm that your IAM user can call ec2:RunInstances and ec2:TerminateInstances, that the service-linked role for Spot exists if you plan to request Spot capacity, and that your GitHub token carries the repo scope, because the action manages runners at the repository level.
Community notes