This repository contains Terraform infrastructure code to deploy scalable, self-hosted GitHub Actions runners on Amazon EC2 instances. The solution provides automated runner provisioning, lifecycle management, and secure deregistration using AWS Auto Scaling Groups, Lambda functions, and CloudWatch logging.
- Build Secure GitHub Self-Hosted Runners on Amazon EC2 with Terraform - Registration process
- Automated GitHub Self-Hosted Runner Cleanup: Lambda Functions and Auto Scaling Lifecycle Hooks - Deregistration process
- Features
- Architecture
- Prerequisites
- Usage
- Configuration
- Security Considerations
- Troubleshooting
- Contributing
- License
- High Availability: Maintains consistent runner capacity using AWS Auto Scaling Groups with automatic instance replacement across multiple Availability Zones
- Secure Authentication: Uses GitHub App authentication for secure API access
- Automated Lifecycle Management: Automatic runner registration via user data script and deregistration using Lambda functions
- Automated Deregistration: Prevents orphaned runners in GitHub organization using lifecycle hooks and Lambda functions
- Unified Logging: Centralized CloudWatch logging for complete runner lifecycle tracking
- Network Security: Runs in private subnets with NAT Gateway for outbound internet access
- Encryption: KMS encryption for secrets, CloudWatch logs, EFS storage, SNS topics, and Lambda functions
- Performance Optimization: EFS with tuned NFS parameters and Lambda layer for reduced cold start times
- Shared Storage: EFS storage for shared runner workspace and dependency caching
The solution deploys:
- VPC with public/private subnets across multiple Availability Zones
- Auto Scaling Group with EC2 instances running GitHub Actions runners
- Auto Scaling Lifecycle Hooks for graceful runner deregistration on instance termination
- SNS Topic for lifecycle event notifications with KMS encryption
- Lambda function for automated runner deregistration via GitHub API
- Lambda Layer with PyJWT and cryptography dependencies for optimized performance
- Dead Letter Queue for Lambda error handling
- EFS file system for shared runner workspace storage with optimized NFS parameters
- CloudWatch log groups for unified lifecycle logging with structured format
- Secrets Manager for secure GitHub App credentials storage
- SSM Parameter Store for NAT Gateway IP addresses
Before deploying this infrastructure, please ensure the following prerequisites are met:
- An AWS account with appropriate permissions to create and manage the resources included in this repository
- An OpenID Connect identity provider created in AWS IAM with a trust relationship to this GitHub repository (detailed setup guide)
- The ARN of the IAM Role stored as a GitHub secret for use in the
terraform.ymlworkflow and referred via${{ secrets.IAM_ROLE }}.
- A GitHub organization where the self-hosted runners will be registered
- A GitHub App created in the organization with the following permissions:
- Repository permissions:
Actions (Read),Administration (Read),Metadata (Read) - Organization permissions:
Self-hosted runners (Write)
- Repository permissions:
- GitHub App credentials (App ID, Installation ID, and Private Key) stored in AWS Secrets Manager
- An
INFRACOST_API_KEYstored as a GitHub Actions secret for cost estimation - A GitHub Actions variable
INFRACOST_SCAN_TYPEset to eitherhcl_codeortf_plandepending on the desired scan type
This infrastructure is deployed automatically using the GitHub Actions workflow defined in .github/workflows/terraform.yml. The workflow provides complete CI/CD automation with security scanning, cost estimation, and infrastructure deployment.
The terraform.yml workflow includes the following automated stages:
- Terraform Format Check: Ensures code follows canonical formatting
- Terraform Validation: Validates configuration syntax and logic
- Terraform Plan: Generates execution plan showing proposed changes
- Plan Output: Posts detailed plan as PR comment for review
- Checkov Security Scan: Runs in separate
code-scan.ymlworkflow to identify security misconfigurations and compliance issues - Infracost Analysis: Provides cost estimates for infrastructure changes
- Cost Comparison: Shows cost diff between current and proposed infrastructure
- Trigger: Automatically deploys on pushes to
mainbranch - Authentication: Uses OIDC for secure, temporary AWS credentials
- Terraform Apply: Provisions infrastructure with GitHub App credentials
- State Management: Maintains Terraform state in remote backend
Set up the following secrets in your GitHub repository:
IAM_ROLE: ARN of the OIDC-assumable IAM roleTHIS_GITHUB_APP_ID: GitHub App ID for runner authenticationTHIS_GITHUB_INSTALLATION_ID: GitHub App Installation IDTHIS_GITHUB_PRIVATE_KEY: GitHub App private keyINFRACOST_API_KEY: API key for cost estimation (optional)
- Workflow Badge: Click the terraform-infra-provisioning badge above for real-time status
- GitHub Actions Logs: Detailed logs available in the Actions tab
- Terraform State: Remote state tracks all deployed resources
- GitHub Organization: Verify runners appear in Actions settings
- CloudWatch Logs: Monitor registration process in
/{name}/lifecyclelog group - Auto Scaling Group: Check EC2 instances are launching successfully
- EFS Mount: Verify shared workspace storage is accessible
The infrastructure can be customized by modifying the default values in variables.tf:
region: AWS region for deployment (default: "us-west-2")name: Prefix for all resource names (default: "github-self-hosted-runner")github_organization: GitHub organization name (must be updated)runner_instance_type: EC2 instance type for runners (default: "t3.medium")runner_min_size: Minimum number of runners in Auto Scaling Group (default: 1)runner_max_size: Maximum number of runners in Auto Scaling Group (default: 3)runner_desired_capacity: Desired number of runners (default: 1)
- All runners operate in private subnets with no direct internet access
- GitHub App authentication provides scoped, time-limited access tokens
- All secrets are encrypted using customer-managed KMS keys
- CloudWatch logs are encrypted at rest with KMS
- EFS file system uses encryption in transit and at rest
- SNS topics and Lambda functions encrypted with customer-managed KMS keys
- Lambda functions run in VPC with private subnets for enhanced security
- Dead Letter Queue encrypted for secure error message handling
- Security groups restrict network access to necessary ports only
- IAM roles follow least privilege principle with minimal required permissions
- Verify GitHub App permissions are correctly configured
- Check CloudWatch logs in
/{name}/lifecyclelog group for registration errors - Ensure GitHub App credentials in Secrets Manager are valid
- Confirm the
github_organizationvariable matches your GitHub organization name
- Check Auto Scaling Group events in AWS Console
- Verify VPC and subnet configuration
- Ensure IAM roles have necessary permissions
- Check CloudWatch logs in
/{name}/lifecycle/{instance-id}/registrationlog stream for detailed startup errors - Review user data script execution in EC2 instance logs
- Check Lambda function logs in CloudWatch
- Verify Dead Letter Queue for failed invocations
- Ensure Lambda has network access to GitHub API
- Confirm GitHub App credentials are accessible from Lambda
- Ensure NAT Gateway is properly configured for private subnet internet access
- Verify NFS security group rules for EFS mount targets
- Check lifecycle hook timeout configuration (5-minute default)
- Verify SNS topic permissions and Lambda subscription configuration
- CloudWatch logs provide detailed lifecycle tracking with structured format
- Auto Scaling Group metrics show scaling activities and lifecycle hook status
- Lambda function metrics indicate deregistration success rates and error patterns
- Dead Letter Queue metrics show failed Lambda executions requiring investigation
- EFS performance metrics monitor storage throughput and connection counts
- SNS topic metrics track message delivery and failure rates
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure that:
- Code follows Terraform best practices
- All resources include appropriate tags
- Security considerations are addressed
- Documentation is updated for any new features
This code is released under the Unlicense License. See LICENSE for details.
