github-action
Initial Setup Ecs

🚀 GitHub Actions Self-Hosted Runners on ECS EC2

Architecture Overview

GitHub Actions → ECS EC2 Cluster → Multiple Runner Containers → RDS

              Auto Scaling Group (2-10 instances)
              Multiple AZs for HA
              ~$30-40/month vs $60+ for Fargate

Step 1: Same Dockerfile and Startup Script

Use the same Dockerfile and start.sh from before - they work for both Fargate and EC2.---

Step 2: CloudFormation Alternative (EC2 Launch Type)---

Step 3: Deployment Guide

Prerequisites

# 1. Build and push Docker image (same as before)
cd github-runner-ecs
chmod +x build-push.sh
./build-push.sh
 
# 2. Create/select EC2 key pair
aws ec2 create-key-pair --key-name github-runner-key --query 'KeyMaterial' --output text > github-runner-key.pem
chmod 400 github-runner-key.pem

Deploy with Terraform

Create terraform.tfvars:

aws_region            = "us-east-1"
github_repo           = "myorg/myrepo"
github_token          = "ghp_xxxxxxxxxxxx"
vpc_id                = "vpc-xxxxx"
private_subnet_ids    = ["subnet-xxxxx", "subnet-yyyyy"]
rds_security_group_id = "sg-xxxxx"
key_name              = "github-runner-key"

Deploy:

cd terraform
terraform init
terraform plan
terraform apply

Deploy with CloudFormation

aws cloudformation create-stack \
  --stack-name github-actions-runners-ec2 \
  --template-body file://cloudformation-ec2.yaml \
  --parameters \
    ParameterKey=VpcId,ParameterValue=vpc-xxxxx \
    ParameterKey=PrivateSubnetIds,ParameterValue="subnet-xxxxx\,subnet-yyyyy" \
    ParameterKey=RDSSecurityGroupId,ParameterValue=sg-xxxxx \
    ParameterKey=GitHubRepo,ParameterValue=myorg/myrepo \
    ParameterKey=GitHubToken,ParameterValue=ghp_xxxxxxxxxxxx \
    ParameterKey=KeyName,ParameterValue=github-runner-key \
    ParameterKey=InstanceType,ParameterValue=t3.medium \
    ParameterKey=MinInstances,ParameterValue=2 \
    ParameterKey=MaxInstances,ParameterValue=6 \
    ParameterKey=DesiredRunnerCount,ParameterValue=4 \
  --capabilities CAPABILITY_NAMED_IAM
 
# Monitor stack creation
aws cloudformation wait stack-create-complete --stack-name github-actions-runners-ec2

Step 4: Verify Deployment

# Check ECS cluster
aws ecs list-clusters
 
# Check EC2 instances in cluster
aws ecs list-container-instances --cluster github-actions-runners
 
# Check running tasks
aws ecs list-tasks --cluster github-actions-runners
 
# Check Auto Scaling Group
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names github-runner-ecs-asg
 
# View logs
aws logs tail /ecs/github-actions-runner --follow

SSH into EC2 Instance (for debugging)

# Get instance IP
INSTANCE_ID=$(aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names github-runner-ecs-asg \
  --query 'AutoScalingGroups[0].Instances[0].InstanceId' \
  --output text)
 
INSTANCE_IP=$(aws ec2 describe-instances \
  --instance-ids $INSTANCE_ID \
  --query 'Reservations[0].Instances[0].PrivateIpAddress' \
  --output text)
 
# SSH (if in same VPC or via bastion)
ssh -i github-runner-key.pem ec2-user@$INSTANCE_IP
 
# Check ECS agent
sudo systemctl status ecs
 
# Check running containers
docker ps
 
# View ECS config
cat /etc/ecs/ecs.config

Step 5: Cost Comparison

EC2 vs Fargate Pricing (Monthly)

ConfigurationEC2 (t3.medium)Fargate
2 instances, 4 runners~$30/month~$60/month
4 instances, 8 runners~$60/month~$120/month
6 instances, 12 runners~$90/month~$180/month

EC2 Breakdown (t3.medium in us-east-1):

  • Instance cost: $0.0416/hour × 730 hours = $30.37/month per instance
  • 2 instances = $60.74/month
  • Each instance can run ~2-4 runner containers

Savings: ~50% cheaper than Fargate!


Step 6: Test Workflow---

Step 7: Scaling Configuration

How Scaling Works

Two-level scaling:

  1. ECS Service Scaling (Task count): 4 → 12 containers
  2. EC2 Auto Scaling (Instance count): 2 → 6 instances
# Manually scale tasks
aws ecs update-service \
  --cluster github-actions-runners \
  --service github-actions-runner \
  --desired-count 8
 
# Manually scale instances
aws autoscaling set-desired-capacity \
  --auto-scaling-group-name github-runner-ecs-asg \
  --desired-capacity 4

Step 8: Monitoring DashboardMake it executable and run:

chmod +x monitor-runners.sh
./monitor-runners.sh

Step 9: Cost Optimization Tips

  1. Use Spot Instances (70% cheaper):
# In Launch Template
instance_market_options {
  market_type = "spot"
  spot_options {
    max_price = "0.03"  # ~70% discount
    spot_instance_type = "one-time"
  }
}
  1. Use Smaller Instances for Low Traffic:

    • t3.small (2 vCPU, 2GB): $15/month - Run 1-2 runners
    • t3.medium (2 vCPU, 4GB): $30/month - Run 2-4 runners ✅ Recommended
    • t3.large (2 vCPU, 8GB): $60/month - Run 4-8 runners
  2. Schedule Scaling:

# Scale down at night
aws autoscaling put-scheduled-action \
  --auto-scaling-group-name github-runner-ecs-asg \
  --scheduled-action-name scale-down-night \
  --recurrence "0 22 * * *" \
  --desired-capacity 1 \
  --min-size 1
 
# Scale up in morning
aws autoscaling put-scheduled-action \
  --auto-scaling-group-name github-runner-ecs-asg \
  --scheduled-action-name scale-up-morning \
  --recurrence "0 8 * * *" \
  --desired-capacity 2 \
  --min-size 2

Production Checklist

  • Docker image built and in ECR
  • ECS cluster created with EC2 capacity provider
  • Auto Scaling Group running (2-6 instances)
  • ECS Service running (4-12 tasks)
  • Security groups configured (ECS → RDS)
  • Runners showing in GitHub (check Settings → Actions → Runners)
  • Test workflow runs successfully
  • CloudWatch logs accessible
  • Auto-scaling tested
  • SSH access configured for debugging
  • Cost monitoring enabled

Quick Commands Reference

# View cluster status
aws ecs describe-clusters --clusters github-actions-runners
 
# List instances
aws ecs list-container-instances --cluster github-actions-runners
 
# View logs
aws logs tail /ecs/github-actions-runner --follow
 
# Scale service
aws ecs update-service --cluster github-actions-runners \
  --service github-actions-runner --desired-count 8
 
# Scale instances
aws autoscaling set-desired-capacity \
  --auto-scaling-group-name github-runner-ecs-asg \
  --desired-capacity 4
 
# SSH to instance
aws ec2 describe-instances --filters "Name=tag:Name,Values=GitHub Runner ECS Instance" \
  --query 'Reservations[*].Instances[*].[InstanceId,PrivateIpAddress]' \
  --output table

This setup gives you:

  • High availability (multi-AZ)
  • Auto-scaling (2-6 instances, 4-12 runners)
  • 50% cost savings vs Fargate
  • No single point of failure
  • Direct RDS access from VPC