GitHub Actions Self-Hosted Runners: Secure Deployment on AWS
Cloud

GitHub Actions Self-Hosted Runners: Secure Deployment on AWS

August 10, 20259 min readGitHub ActionsSelf-hosted RunnersAWS

GitHub-hosted runners have limits: cost, private network access, performance. Self-hosted runners on EC2 solve these problems — provided you secure them properly.

Why Switch to Self-Hosted Runners?

GitHub-hosted runners (ubuntu-latest, windows-latest) are convenient but limited: no access to private network resources (RDS, ElastiCache, private registries), performance capped at 2 vCPU / 7 GB RAM, and costs that skyrocket as builds become frequent or long.

Self-hosted runners let you use any EC2 instance — more RAM, GPU, VPC access, local Docker or Maven cache. But they introduce security risks that must be managed.

Recommended Architecture on AWS

  • Auto Scaling Group of EC2 instances in a private subnet (no public IP)
  • NAT Gateway for outbound calls (GitHub API, action downloads)
  • IAM Instance Profile with minimum permissions needed for builds
  • Scale-to-zero: 0 instances at rest, scale-out triggered by GitHub webhook via Lambda
  • Ephemeral mode mandatory: each job runs on a fresh instance (--ephemeral), terminated after execution

Security: Non-Negotiable Rules

1. Always Use Ephemeral Mode

Without --ephemeral, a malicious job can leave files, environment variables, or credentials in the runner environment for the next job. In ephemeral mode, the runner deregisters and the instance is terminated after each job.

2. Only Use Self-Hosted Runners on Private Repositories

On a public repository, anyone can submit a PR that triggers a workflow on your runner, potentially exposing your IAM secrets or internal network. GitHub explicitly advises against this.

3. Minimal IAM Permissions

Grant only the permissions needed for the specific build tasks — ECR push/pull, S3 access, SSM parameters. Never use AdministratorAccess on a runner instance profile.

4. Network Security

  • Security Group: no inbound rules — runners always initiate the connection to GitHub
  • Outbound: HTTPS (443) to github.com, *.actions.githubusercontent.com, ECR endpoint
  • VPC Endpoint for ECR to avoid internet traversal for image push/pull

5. Token Rotation

Never store the GitHub registration token in code or unencrypted environment variables. Use AWS Secrets Manager or SSM Parameter Store (SecureString) with a Lambda that generates a new token via the GitHub API on each instance start.

Scale-to-Zero with Lambda + Webhook

The most cost-effective solution: 0 runners at rest, auto scale-out on receiving a workflow_job webhook from GitHub — a Lambda triggered by API Gateway sets the ASG desired capacity to 1 when a job is queued, and back to 0 after completion.

Managed Alternative: Actions Runner Controller (ARC)

For teams on Kubernetes, Actions Runner Controller (ARC) deploys runners as ephemeral Kubernetes pods. It integrates with KEDA for scale-to-zero and automatically handles registration/deregistration. With EKS + Spot nodes, it's the most cost-effective solution for variable workloads.

Conclusion

Self-hosted runners on AWS combine GitHub Actions flexibility with private resource access and EC2 performance. The key is discipline: ephemeral mode mandatory, minimal IAM, private repositories only, scale-to-zero for cost control.

← Back to blog