GitHub Actions CI/CD Setup Guide
This guide walks you through setting up automatic deployment to AWS ECS when code is pushed to the staging branch.
📋 Prerequisites
- AWS Account with ECS, ECR, and IAM access
- GitHub repository with
stagingbranch - AWS resources already created (ECR repository, ECS cluster, ECS service)
🔐 Step 1: Create IAM User for GitHub Actions
Why: GitHub Actions needs AWS credentials to push images to ECR and update ECS services.
-
AWS Console → IAM → Users → Create user
-
User name:
github-actions-deploy- ✅ Provide user access to the AWS Management Console - Unchecked (not needed)
- ✅ Access key - Programmatic access - Checked (required)
-
Set permissions → Attach policies directly
-
Add these policies:
AmazonEC2ContainerRegistryFullAccess(for ECR push)AmazonECS_FullAccess(for ECS deployment)AmazonEC2ContainerServiceFullAccess(alternative, broader access)
OR create custom policy (more secure - recommended):
Create policy → JSON → Paste:
Option 1: Staging-Only Policy (if using separate IAM users per environment):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices",
"ecs:DescribeTaskDefinition",
"ecs:DescribeTasks",
"ecs:ListTasks",
"ecs:RegisterTaskDefinition",
"ecs:UpdateService"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": [
"arn:aws:iam::606532921651:role/reelog-ecs-task-execution-role",
"arn:aws:iam::606532921651:role/reelog-ecs-task-role"
]
}
]
}Option 2: Environment-Agnostic Policy (if using same IAM user for staging + production):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices",
"ecs:DescribeTaskDefinition",
"ecs:DescribeTasks",
"ecs:ListTasks",
"ecs:RegisterTaskDefinition",
"ecs:UpdateService"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": [
"arn:aws:iam::606532921651:role/reelog-ecs-task-execution-role",
"arn:aws:iam::606532921651:role/reelog-ecs-task-role",
"arn:aws:iam::606532921651:role/reelog-ecs-task-execution-role-prod",
"arn:aws:iam::606532921651:role/reelog-ecs-task-role-prod"
]
}
]
}Recommendation: Use Option 2 (Environment-Agnostic) if you plan to deploy to both staging and production with the same IAM user. This allows one policy to work for both environments.
- Policy name:
GitHubActionsDeployPolicy - Description:
Allows GitHub Actions to build and push Docker images to ECR, and deploy updates to ECS services. Grants minimal permissions required for CI/CD pipeline. - Tags (optional but recommended):
- Tag 1: Key=
Environment, Value=Staging(orAllif using Option 2) - Tag 2: Key=
Project, Value=Reelog - Tag 3: Key=
Purpose, Value=GitHubActionsDeployPolicy - Tag 4: Key=
Name, Value=GitHubActionsDeployPolicy
- Tag 1: Key=
- Create policy
-
Attach policy to user → Next → Create user
-
Create Access Key (if not created during user creation):
- IAM Console → Users →
github-actions-deploy→ Security credentials tab - Access keys section → Create access key
- Use case: Select Third-party service
- ✅ Best match - GitHub Actions is a third-party CI/CD service that manages AWS resources
- Alternative: "Application running outside AWS" also works, but "Third-party service" is more specific
- Description (optional):
GitHub Actions CI/CD deployment - Set description tag (optional): Key=
Purpose, Value=GitHubActions - Create access key
- IAM Console → Users →
-
IMPORTANT: Copy the Access Key ID and Secret Access Key
- ⚠️ You can only see the secret key once!
- ⚠️ Download the .csv file or copy both values immediately
- Save these securely - you'll add them to GitHub Secrets
-
Tags (optional but recommended):
- Tag 1: Key=
Environment, Value=Staging - Tag 2: Key=
Project, Value=Reelog - Tag 3: Key=
Purpose, Value=GitHubActions - Tag 4: Key=
Name, Value=github-actions-deploy
- Tag 1: Key=
🔑 Step 2: Add GitHub Secrets
Why: Store AWS credentials securely in GitHub (encrypted, not visible in logs).
-
GitHub Repository → Settings → Secrets and variables → Actions
-
New repository secret → Add each of these:
Secret 1:
- Name:
AWS_ACCESS_KEY_ID - Value: Your IAM user Access Key ID (from Step 1.6)
- Add secret
Secret 2:
- Name:
AWS_SECRET_ACCESS_KEY - Value: Your IAM user Secret Access Key (from Step 1.6)
- Add secret
- Name:
📝 Step 3: Verify Workflow File
The workflow file is already created at:
.github/workflows/deploy-staging.yml
What it does:
- ✅ Triggers on push to
stagingbranch - ✅ Builds Docker image
- ✅ Pushes to ECR (
reelog-api-staging) - ✅ Updates ECS task definition with new image
- ✅ Deploys to ECS service (
reelog-api-staging) - ✅ Waits for service to stabilize
Configuration:
- AWS Region:
us-east-2 - ECR Repository:
reelog-api-staging - ECS Cluster:
reelog-staging - ECS Service:
reelog-api-staging - Task Definition:
reelog-api-staging - Container Name:
reelog-api
To customize: Edit .github/workflows/deploy-staging.yml and update the env section if needed.
🚀 Step 4: Test Deployment
-
Make a change to your code (e.g., update a comment)
-
Commit and push to staging branch:
git checkout staging
git add .
git commit -m "Test: GitHub Actions deployment"
git push origin staging -
Check GitHub Actions:
- GitHub Repository → Actions tab
- You should see "Deploy to Staging" workflow running
- Click on it to see progress
-
Monitor deployment:
- Watch the workflow logs
- Check AWS ECS Console →
reelog-stagingcluster →reelog-api-stagingservice - New tasks should be deploying
-
Verify:
curl https://api-staging.reelog.app/ping
🔍 Troubleshooting
Workflow fails at "Login to Amazon ECR"
- Check: AWS credentials are correct in GitHub Secrets
- Check: IAM user has
ecr:GetAuthorizationTokenpermission
Workflow fails at "Build, tag, and push image"
- Error: "tag invalid: The image tag 'latest' already exists and cannot be overwritten because the tag is immutable"
- Cause: ECR repository has immutable tags enabled (security best practice)
- Solution: The workflow now only pushes Git SHA tags (not
latest). This is actually better practice - each deployment uses a specific, traceable image tag. - Note: If you need to change this, you can disable immutable tags in ECR settings, but it's not recommended for security reasons.
- Error: Access denied or permission errors
- Check: IAM user has ECR push permissions (
ecr:PutImage,ecr:InitiateLayerUpload, etc.) - Check: ECR repository name matches (
reelog-api-staging)
- Check: IAM user has ECR push permissions (
- Error: Dockerfile not found
- Check: Dockerfile exists in repository root
Workflow fails at "Deploy Amazon ECS task definition"
- Error: "Unexpected key 'enableFaultInjection' found in params"
- Cause: Task definition downloaded from ECS contains metadata fields that the GitHub Action doesn't support
- Solution: The workflow now includes a step to clean unsupported fields automatically
- If still failing: Check that
jqis available (it's pre-installed on GitHub Actions runners)
- Error: "AccessDenied" or permission errors
- Check: IAM user has ECS update permissions
- Check: IAM user has
iam:PassRolepermission for task execution role
- Error: Service or container name mismatch
- Check: ECS service name matches (
reelog-api-staging) - Check: Container name in task definition matches (
reelog-api)
- Check: ECS service name matches (
Deployment succeeds but service doesn't update
- Check: ECS service is using the correct task definition family
- Check: Service auto-deployment is enabled
- Check: New tasks are healthy (check CloudWatch logs)
📊 Workflow Features
Automatic Triggers
- ✅ Push to staging branch - Automatic deployment
- ✅ Manual trigger - Use "Run workflow" button in Actions tab
Image Tagging
- ✅ Git SHA tag -
606532921651.dkr.ecr.us-east-2.amazonaws.com/reelog-api-staging:abc123... - ✅ Latest tag -
606532921651.dkr.ecr.us-east-2.amazonaws.com/reelog-api-staging:latest - Both tags are pushed for flexibility
Deployment Safety
- ✅ Waits for service stability - Ensures new tasks are healthy before completing
- ✅ Non-destructive - Updates service with zero-downtime deployment
- ✅ Rollback capability - Can manually update service to previous task definition if needed
🔒 Security Best Practices
- Use custom IAM policy (not full access) - Limits permissions to only what's needed
- Rotate credentials regularly - Update GitHub Secrets every 90 days
- Monitor IAM user activity - Check CloudTrail logs for unusual activity
- Use separate IAM users - One for staging, one for production (when you set up prod)
- Never commit secrets - All credentials stored in GitHub Secrets only
📝 Next Steps
- ✅ Staging deployment is now automated!
- 🔄 Consider setting up production deployment workflow (similar, but for
mainbranch) - 📊 Set up deployment notifications (Slack, email, etc.)
- 🔍 Add automated tests before deployment
- 📈 Monitor deployment metrics in CloudWatch