- Architecture Overview and Infrastructure Components
- ECR Runner Image Repository
- Self-hosted GitHub Runner(s) Registration Token
- Hosting the Runner Docker Artifacts on CodeCommit
- Build/Push Runner Image using CodeBuild
- Scalable ECS Cluster
- EventBus and Schema Discover for Webhook Events
- ECS Runner Task Definition
- Lambda Function URL
- GitHub Webhook
- EventBridge Rule
- Testing the Final Infrastructure
This blog post covers setting up a CodeBuild project to build and push the GitHub runner docker image to ECR.
To ensure that the version of the GitHub runner agent within the image remains current, the Codebuild project includes a check to compare the installed version with the latest official release. An EventBridge rule will need to be established to trigger the build on a daily basis using a cron expression.
The project name of github-actions-self-hosted-runner-debian-build is used for the sample commands which follow. AWS account references have been replaced with xxxxxxxxxxxx. Be sure to replace these references with your target account.
All commands were executed using the AWS CLI.
Project IAM Service Role
Create Role
File $HOME/github-actions-role-trust-policy.json below contains the trust policy definition required for the role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Create the role, with the trust policy attached:
aws iam create-role \
--path "/service-role/" \
--role-name github-actions-self-hosted-runner-debian-build-role \
--assume-role-policy-document file://$HOME/github-actions-role-trust-policy.json
Output sample:
{
"Role": {
"Path": "/service-role/",
"RoleName": "github-actions-self-hosted-runner-debian-build-role",
"RoleId": "AROARZPUZDIKD3RBBD2BX",
"Arn": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/github-actions-self-hosted-runner-debian-build-role",
"CreateDate": "2023-10-10T05:26:51.539000+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"MaxSessionDuration": 3600,
"RoleLastUsed": {}
}
}
IAM Policy
Create the file $HOME/github-actions-role-iam-policy.json as shown below.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"codebuild:StopBuild",
"codebuild:StartBuild",
"codebuild:RetryBuild"
],
"Resource": "arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:project/github-actions-self-hosted-runner-debian-build"
}, {
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:build/github-actions-self-hosted-runner-debian-build:*"
}, {
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecr:GetAuthorizationToken"
],
"Resource": "*"
}, {
"Effect": "Allow",
"Action": [
"codebuild:BatchPutCodeCoverages",
"codebuild:BatchPutTestCases",
"codebuild:CreateReport",
"codebuild:CreateReportGroup",
"codebuild:UpdateReport",
"codecommit:GitPull",
"codecommit:GitPush",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchDeleteImage",
"ecr:BatchGetImage",
"ecr:CompleteLayerUpload",
"ecr:DescribeImages",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:ListImages",
"ecr:PutImage",
"ecr:PutImageScanningConfiguration",
"ecr:PutImageTagMutability",
"ecr:UploadLayerPart",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:report-group/github-actions-self-hosted-runner-debian-build-*",
"arn:aws:codecommit:us-east-1:xxxxxxxxxxxx:github-actions-self-hosted-runner-debian",
"arn:aws:ecr:us-east-1:xxxxxxxxxxxx:repository/github-actions-self-hosted-runner-debian",
"arn:aws:logs:us-east-1:xxxxxxxxxxxx:log-group:github-actions-self-hosted-runner-debian-codebuild",
"arn:aws:logs:us-east-1:xxxxxxxxxxxx:log-group:github-actions-self-hosted-runner-debian-codebuild:*"
]
}
]
}
Attach the policy to the role created in previous section:
aws iam put-role-policy \
--role-name github-actions-self-hosted-runner-debian-build-role \
--policy-name github_actions_self_hosted_runner_policy \
--policy-document file://$HOME/github-actions-role-iam-policy.json
ECR Repository Policy
Since our CodeBuild project needs to pull/push the GitHub Runner image from ECR, a policy allowing it to do so will be required.
Create the file $HOME/github-actions-ecr-policy.json with definition as shown below.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CodeBuildAccessPrincipal",
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Condition": {
"StringEquals": {
"aws:SourceAccount": "xxxxxxxxxxxx",
"aws:SourceArn": "arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:project/github-actions-self-hosted-runner-debian-build"
}
}
}
]
}
Apply the policy to the ECR repository github-actions-self-hosted-runner-debian that was created in previous post:
aws ecr set-repository-policy \
--repository-name github-actions-self-hosted-runner-debian \
--policy-text file://$HOME/github-actions-ecr-policy.json
Create CodeBuild Project
The sample CLI input file $HOME/github-actions-self-hosted-runner-debian_codebuild_project.json is used to create the project.
{
"name": "github-actions-self-hosted-runner-debian-build",
"description": "Build Github runner Docker image from codecommit repository",
"source": {
"type": "CODECOMMIT",
"location": "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/github-actions-self-hosted-runner-debian",
"buildspec": "./buildspec.yml"
},
"sourceVersion": "refs/heads/master",
"artifacts": {
"type": "NO_ARTIFACTS"
},
"environment": {
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:5.0",
"computeType": "BUILD_GENERAL1_SMALL",
"environmentVariables": [{
"name": "AWS_DEFAULT_REGION",
"value": "us-east-1",
"type": "PLAINTEXT"
}, {
"name": "AWS_ACCOUNT_ID",
"value": "xxxxxxxxxxxx",
"type": "PLAINTEXT"
}, {
"name": "IMAGE_TAG",
"value": "latest",
"type": "PLAINTEXT"
}, {
"name": "IMAGE_REPO_NAME",
"value": "github-actions-self-hosted-runner-debian",
"type": "PLAINTEXT"
}
],
"imagePullCredentialsType": "CODEBUILD",
"privilegedMode": true
},
"serviceRole": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/github-actions-self-hosted-runner-debian-build-role",
"timeoutInMinutes": 60,
"queuedTimeoutInMinutes": 60,
"tags": [{
"key": "codebuild-project",
"value": "github-actions-self-hosted-runner-debian-build"
}
],
"badgeEnabled": false,
"logsConfig": {
"cloudWatchLogs": {
"status": "ENABLED",
"groupName": "github-actions-self-hosted-runner-debian-codebuild",
"streamName": "github-actions-self-hosted-runner-debian"
}
},
"buildBatchConfig": {
"serviceRole": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/github-actions-self-hosted-runner-debian-build-role",
"combineArtifacts": true,
"restrictions": {
"maximumBuildsAllowed": 100,
"computeTypesAllowed": [
"BUILD_GENERAL1_SMALL",
"BUILD_GENERAL1_MEDIUM"
]
},
"timeoutInMins": 60
}
}
Run the following to create the project:
aws codebuild create-project --cli-input-json file://$HOME/github-actions-self-hosted-runner-debian_codebuild_project.json
Build Trigger Prerequisites
First, we start by creating IAM roles and policies required for the build trigger.
EventBridge Role and Trust Policy
File $HOME/github-actions-role-events-trust-policy.json below contains the trust policy definition required for the role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Create the role with a name of github-actions-codebuild-trigger-role :
aws iam create-role \
--path "/service-role/" \
--role-name github-actions-codebuild-trigger-role \
--assume-role-policy-document file://$HOME/github-actions-role-events-trust-policy.json
IAM Policy for Trigger
Create the policy json $HOME/github-actions-role-events-iam-policy.json as shown below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codebuild:StartBuild"
],
"Resource": [
"arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:project/github-actions-self-hosted-runner-debian-build"
]
}
]
}
Attach the policy to the EventBridge role created in previous section:
aws iam put-role-policy \
--role-name github-actions-codebuild-trigger-role \
--policy-name github_actions_role_events_iam_policy \
--policy-document file://$HOME/github-actions-role-events-iam-policy.json
Create Build Trigger
Create a cron-based expression rule run daily at 6:00AM UTC time:
aws events put-rule \
--name run-github_actions-self-hosted-runner-debian-codebuild \
--schedule-expression 'cron(0 6 * * ? *)' \
--role-arn arn:aws:iam::xxxxxxxxxxxx:role/service-role/github-actions-codebuild-trigger-role
Set the target for the rule to be our CodeBuild project:
aws events put-targets \
--rule run-github_actions-self-hosted-runner-debian-codebuild \
--targets '
[
{
"Id": "trigger_codebuild",
"Arn": "arn:aws:codebuild:us-east-1:xxxxxxxxxxxx:project/github-actions-self-hosted-runner-debian-build",
"RoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/github-actions-codebuild-trigger-role"
}
]
'
Testing the Build
There are two scenarios to cover for testing:
- ECR image has an outdated runner agent installed
- ECR image has the latest runner agent installed
ECR Image has an Outdated Runner Agent Installed
For this test case, the image is expected to be rebuilt using the latest agent version before being pushed to ECR.
- Choose “Start build with overrides”

- Choose the following options to configure and start the build

- The following shows Phase details for a successful build

- Check the Build logs

- From output above:
- the Github runner agent version installed within the ECR image is: 2.309.0
- a check of the latest runner agent version that’s currently available for download from the official repository is: 2.310.2

- Since a newer release of the agent has been released , the image is rebuilt with the newer version, and pushed to ECR
ECR Image has the Latest Runner Agent Installed
When the image already contains the latest runner agent version, build and push to ECR should be skipped.
Rerunning the build will allow us to verify that the build/push steps to ECR are skipped.
The following is the output log produced after a rerun:

As expected, since the image was already packaged with the latest agent, the build/push to ECR steps were skipped.