Skip to content

Repository files navigation

GitHub Actions runners on AWS Lambda MicroVMs

Terraform module (terraform-aws-github-runner-lambda-microvms) that runs ephemeral, auto-scaling GitHub Actions self-hosted runners inside AWS Lambda MicroVMs - Firecracker-isolated, snapshot-resumable, Graviton/arm64 compute.

A webhook-driven dispatcher launches one Lambda MicroVM per queued CI job; the VM resumes from a prebuilt snapshot in seconds, registers a single-use runner (JIT runner), runs the job (Docker-in-runner supported), and terminates itself the moment the job ends - so you never pay for idle runners. One terraform apply builds the image, deploys the dispatcher behind a public Lambda Function URL, and (optionally) wires the GitHub webhook.

See docs/ARCHITECTURE.md for how it works and the cost comparison vs GitHub-hosted runners, and docs/USAGE.md for the full setup guide.

Usage

provider "aws" {
  region = "eu-west-1" # a region where Lambda MicroVMs are available
}

provider "awscc" {
  region = "eu-west-1" # same region as aws; builds the MicroVM image
}

# Only needed when manage_webhooks = true.
provider "github" {
  owner = "my-org"
  app_auth {
    id              = "123456"
    installation_id = "12345678"
    pem_file        = file("app.pem")
  }
}

module "gha_runner" {
  source  = "mkdev-me/github-runner-lambda-microvms/aws"
  version = "~> 1.0.0"

  github_app = {
    app_id          = "123456"
    installation_id = "12345678"
    private_key     = file("app.pem") # PEM contents
  }

  github_organization = "my-org"
  manage_webhooks     = true
}

Then run a workflow with runs-on: [self-hosted, linux, arm64, microvm]. A runnable copy is in examples/github-app.

Prerequisites

  • A GitHub App - the dispatcher's only credential (App ID, installation ID, private key). You create it once; see docs/USAGE.md#you-need-a-github-app.
  • On the machine that runs terraform apply (the MicroVM image is a native Cloud Control resource; only the dispatcher zip is built via local-exec): Terraform ≥ 1.9, AWS credentials, python3 + pip, zip, bash.

Notes & caveats

  • arm64 / Graviton only - Lambda MicroVMs are Graviton-only, so runners are linux-arm64.
  • Public ingress - the default Lambda Function URL is internet-facing; request authenticity rests on the X-Hub-Signature-256 HMAC check.
  • Not fully hermetic - the dispatcher zip is built with pip/zip via local-exec at apply time.
  • First apply takes a few minutes - it waits for the MicroVM image build to reach ACTIVE.

Requirements

Name Version
terraform >= 1.9.0
archive >= 2.4
aws >= 6.0, < 7.0
awscc >= 1.0
github >= 6.2
random >= 3.6

Providers

Name Version
archive 2.8.0
aws 6.52.0
awscc 1.90.0
github 6.12.1
random 3.9.0
terraform n/a

Resources

Name Type
aws_cloudwatch_log_group.dispatcher resource
aws_cloudwatch_log_group.runner resource
aws_iam_role.build resource
aws_iam_role.dispatcher resource
aws_iam_role.exec resource
aws_iam_role_policy.build resource
aws_iam_role_policy.dispatcher resource
aws_iam_role_policy.exec resource
aws_lambda_function.dispatcher resource
aws_lambda_function_url.webhook resource
aws_s3_bucket.artifacts resource
aws_s3_bucket_public_access_block.artifacts resource
aws_s3_bucket_server_side_encryption_configuration.artifacts resource
aws_s3_object.microvm_code resource
aws_ssm_parameter.dispatcher resource
awscc_lambda_microvm_image.runner resource
github_organization_webhook.runner resource
github_repository_webhook.runner resource
random_password.webhook resource
terraform_data.dispatcher_build resource

Inputs

Name Description Type Default Required
github_app GitHub App credentials - the dispatcher's machine identity. app_id: numeric App ID as a string, e.g. "123456" (NOT the Client ID). installation_id: App installation ID. Optional for the dispatcher (it derives it per-repo), but REQUIRED when manage_webhooks = true because the github provider's app_auth block needs it. private_key: App private key PEM contents (not a file path). object({ app_id = string installation_id = optional(string) private_key = string }) n/a yes
additional_os_capabilities additionalOsCapabilities for the MicroVM image. ["ALL"] enables nested Docker / privileged ops (needed for docker/services: jobs). Set [] to tighten for non-Docker workloads. list(string) [ "ALL" ] no
artifacts_bucket_name Override the S3 artifacts bucket name. Default (null) creates one named '<name_prefix>-artifacts-<account_id>'. string null no
base_image_name AWS-managed MicroVM base image short name, resolved to arn:aws:lambda::aws:microvm-image:. string "al2023-1" no
base_image_version Major version of the AWS-managed base image (base_image_name), as a single number (e.g. "0"). Cloud Control requires it and validates the format. string "0" no
dispatcher_memory_size Dispatcher Lambda memory (MB). number 256 no
dispatcher_timeout Dispatcher Lambda timeout (seconds). number 30 no
github_api_url GitHub REST API base URL. Set for GitHub Enterprise Server. -> GH_API_URL. string "https://api.github.com" no
github_organization GitHub org (or user) that owns the runners/webhooks. Used as the github provider owner in your root module, and (with empty github_repositories) selects an org-level webhook. string null no
github_repositories Repositories to attach runner webhooks to, as 'owner/name'. Empty => an org-level webhook (requires github_organization). All entries must belong to the github provider's owner. list(string) [] no
image_version Pin the runner image version the dispatcher launches (e.g. "3.0"). Default (null) makes the dispatcher resolve the latest ACTIVE version at runtime, so image rebuilds are picked up with no redeploy. string null no
log_retention_days CloudWatch retention (days) for the dispatcher and runner log groups. number 14 no
manage_webhooks If true, Terraform creates the workflow_job webhooks via the github provider (configure it in your root module). If false, wire them up yourself from the webhook_payload_url + webhook_secret outputs. bool true no
max_duration_seconds maximumDurationInSeconds passed to RunMicrovm - the hard cap on one job's runtime, after which the MicroVM is auto-terminated (cost backstop). number 1200 no
name_prefix Prefix for every created resource (roles, lambda, secret, bucket, image). string "gha-microvm" no
required_labels Labels a workflow_job must ALL carry for the dispatcher to launch a MicroVM (subset match). -> REQUIRED_LABELS. list(string) [ "self-hosted", "microvm" ] no
runner_labels Labels the ephemeral runner registers with (arm64 only). -> RUNNER_LABELS. list(string) [ "self-hosted", "linux", "arm64", "microvm" ] no
runner_memory_mib MicroVM memory (minimumMemoryInMiB) baked into the image. Tiers: 512,1024,2048,4096,8192,... Defaults to 8192 because the default image enables Docker (additional_os_capabilities = ["ALL"]) and real Docker builds OOM at less; drop to 4096/2048 for lightweight jobs to cut cost. number 8192 no
tags Tags applied to all taggable resources. map(string) {} no

Outputs

Name Description
artifacts_bucket Name of the S3 bucket holding the MicroVM code artifact.
dispatcher_function_name Name of the dispatcher Lambda function.
exec_role_arn ARN of the MicroVM execution role (executionRoleArn for RunMicrovm).
image_arn ARN of the MicroVM image (imageIdentifier for RunMicrovm).
image_version Active version of the MicroVM image (e.g. '8.0'); increments on each rebuild.
secret_param_arn ARN of the SSM SecureString parameter.
secret_param_name Name of the SSM Parameter Store SecureString holding the webhook secret + GitHub credential.
webhook_payload_url Payload URL for the GitHub webhook (content type: application/json). Already wired up when manage_webhooks = true.
webhook_secret HMAC secret shared with GitHub (X-Hub-Signature-256). Sensitive: read with terraform output -raw webhook_secret. Set as the webhook 'Secret' if wiring up manually (manage_webhooks = false).

Examples

Documentation

  • docs/USAGE.md - full setup: creating the GitHub App, all inputs/options, ingress, scope, runbook.
  • docs/ARCHITECTURE.md - deep dive: components, snapshot/DNS/self-terminate mechanics, cost model.

License

MIT.

About

Run fully serverless billed per-second GHA Runners on Lambda MicroVMs

Resources

Contributing

Stars

27 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages