A Terraform module that backs up all repositories in a GitHub organization to S3 using an ECS Fargate scheduled task. Designed to be deployed by the customer in their own AWS account with zero operational dependency on InfraHouse.
- No always-on compute -- Fargate runs on a schedule, you only pay for backup time
- No Lambda timeout limits -- large organizations with many repos back up without issues
- Customer-owned GitHub App -- no shared credentials, short-lived tokens only
- Cross-region replication -- S3 replication for disaster recovery
- Full git history -- uses
git bundlefor complete, portable backups
- ECS Fargate scheduled task (EventBridge) for daily/custom-schedule backups
- S3 bucket with versioning and configurable retention lifecycle
- Cross-region S3 replication (AWS provider v6, no aliased providers)
- CloudWatch Logs, metrics, and alarm on backup failure
- Least-privilege IAM roles for task execution and task runtime
- Customer creates and owns their own GitHub App (read-only access)
- Create a GitHub App in your organization (see Getting Started)
- Deploy the module -- the module creates a Secrets Manager secret for the App private key:
module "github_backup" {
source = "registry.infrahouse.com/infrahouse/github-backup/aws"
version = "2.0.3"
github_app_id = "123456"
github_app_installation_id = "78901234"
alarm_emails = ["devops@example.com"]
github_app_key_secret_writers = [aws_iam_role.deployer.arn]
replica_region = "us-east-1"
subnets = ["subnet-abc123", "subnet-def456"]
# Optional
schedule_expression = "rate(1 day)"
backup_retention_days = 365
}- Store the App private key in the secret created by the module (output:
github_app_key_secret_arn)
| Name | Version |
|---|---|
| terraform | ~> 1.5 |
| aws | ~> 6.0 |
| Name | Version |
|---|---|
| aws | 6.49.0 |
| Name | Source | Version |
|---|---|---|
| backup_bucket | registry.infrahouse.com/infrahouse/s3-bucket/aws | 0.6.0 |
| github_app_key | registry.infrahouse.com/infrahouse/secret/aws | ~> 1.1 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| alarm_emails | List of email addresses to receive CloudWatch alarm notifications. AWS will send confirmation emails that must be accepted. |
list(string) |
n/a | yes |
| backup_retention_days | Number of days to retain backups in S3 before expiration. Set to 0 to disable expiration. |
number |
365 |
no |
| environment | Name of environment. | string |
"development" |
no |
| force_destroy | Allow destroying S3 buckets even when they contain objects. Set to true only for testing. |
bool |
false |
no |
| github_app_id | The GitHub App ID. Found in the App's settings page. | string |
n/a | yes |
| github_app_installation_id | The installation ID of the GitHub App on the target organization. |
string |
n/a | yes |
| github_app_key_secret_writers | List of IAM role ARNs that are allowed to write the GitHub App private key (PEM) into the secret created by this module. |
list(string) |
n/a | yes |
| image_uri | Docker image URI for the backup runner. Defaults to the InfraHouse public ECR image tagged "latest". For production use, consider pinning to a specific commit SHA tag (e.g., "public.ecr.aws/infrahouse/github-backup:abc1234") to avoid unexpected changes. |
string |
"public.ecr.aws/infrahouse/github-backup:latest" |
no |
| log_group_kms_key_arn | ARN of a KMS key to encrypt the CloudWatch Log Group. If null, logs are encrypted with the default AWS-managed key. |
string |
null |
no |
| log_retention_days | Number of days to retain CloudWatch logs. | number |
365 |
no |
| replica_region | AWS region for cross-region backup replication. | string |
n/a | yes |
| s3_bucket_name | Name for the S3 backup bucket. If null, a name is auto-generated. |
string |
null |
no |
| schedule_expression | EventBridge schedule expression for backup frequency. Examples: "rate(1 day)", "cron(0 2 * * ? *)" |
string |
"rate(1 day)" |
no |
| service_name | Descriptive name of the service. Used for naming resources. |
string |
"github-backup" |
no |
| subnets | List of subnet IDs for the Fargate task. The subnets must have outbound internet access (GitHub API, S3, etc.) — either private subnets with a NAT gateway or public subnets. Public IP assignment is detected automatically from the subnet configuration. |
list(string) |
n/a | yes |
| tags | Tags to apply to all resources. | map(string) |
{} |
no |
| task_cpu | CPU units for the Fargate task (1024 = 1 vCPU). | number |
1024 |
no |
| task_ephemeral_storage_gb | Ephemeral storage (GiB) for the Fargate task. Must be large enough to hold the biggest single repository mirror and its git bundle simultaneously. |
number |
50 |
no |
| task_memory | Memory (MiB) for the Fargate task. | number |
2048 |
no |
| Name | Description |
|---|---|
| ecs_cluster_arn | ARN of the ECS cluster. |
| ecs_cluster_name | Name of the ECS cluster. |
| github_app_key_secret_arn | ARN of the Secrets Manager secret for the GitHub App private key. |
| log_group_name | Name of the CloudWatch log group. |
| replica_bucket_arn | ARN of the replica S3 bucket (cross-region). |
| replica_bucket_name | Name of the replica S3 bucket (cross-region). |
| s3_bucket_arn | ARN of the S3 bucket where backups are stored. |
| s3_bucket_name | Name of the S3 bucket where backups are stored. |
| schedule_rule_arn | ARN of the EventBridge schedule rule. |
| security_group_id | ID of the security group for the Fargate task. |
| task_definition_arn | ARN of the ECS task definition. |
| task_role_arn | ARN of the IAM role used by the backup task. |
See CONTRIBUTING.md for guidelines.