Self-hosted GitHub Actions on ECS

How do Self-hosted GitHub Runners work?

GitHub Runners are a service that long polls GitHub for work. They reset the connection every 50 seconds. Before starting they need to be registered via the API and configured. They don’t take incoming connections and just need to make outbound connections to GitHub (and any other dependencies). This is great as the runners don’t need to be internet facing.


The first step is to “dockerise” the GitHub, luckily @myoung34 has done this for already myoung34/docker-github-actions-runner. His implementation takes some environment variables below, registers and starts the runner when the container is started.

Environment Variables

A subset of @myoung34’s environment variables:

Deploying to ECS

Deploying ECS had a couple of gotchas/requirements:

  • You have to put your GitHub PAT in SSM to make it securely available to the task
  • You need to spin up/deploy to a ECS cluster that can access GitHub and the outside world
  • You need to consider/setup your ECS Instance Role, Execution Role and Task Roles in IAM

Task Definition

This makes for a very straightforward ECS Task Definition:

GitHub Actions Settings

After you deploy the task definition as an x5 service to ECS you get:

Testing with a workflow

It works! 🎉

It successfully triggers the workflow and picks up the task role: arn:aws:iam:::role/gha-taskrole.

"AccessKeyId": "ACCESS_KEY_ID",
"Expiration": "EXPIRATION_DATE",
"RoleArn": "TASK_ROLE_ARN",
"SecretAccessKey": "SECRET_ACCESS_KEY",

Considerations and wrap up

Overall I’m pretty happy, it’s a shame the roles don’t magically work but that’s somewhat expected. I will proceed to a production implementation of this. There are still a few unknowns to be discovered: how to recycle runners (they persist between jobs — death would be better but this might affect performance), scaling strategies for the instance/service count, how the GitHub Actions runner labels/groups work in reality and do any maintenance scripts need to get written? I also need to refine the “Docker security options”; I don’t think the container needs full “Privileged” access and setting some SELinux or AppArmour configure would be more refined.



Climber and Software Engineer. Passionate about mountains (especially winter sports), software engineering, people and the trivia in-between.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store