CI/CD for a Go + Gin App to ECS with GitHub Actions
Test, build a Docker image, push to ECR, and deploy your Gin service to ECS on every push to main.
This recipe ships a Go + Gin API to ECS Fargate. CI tests against a Postgres service container, builds and pushes a Docker image to ECR, then updates the ECS service with a new task definition revision.
What the pipeline does
- check out and set up Go
- run go vet and tests against Postgres
- build and push a Docker image to ECR
- render a new ECS task definition
- deploy to the ECS service and wait for stability
The workflow
A Postgres service backs integration tests. After tests pass on main, the image is pushed to ECR and the ECS service is updated with the new image tag.
name: CI
on:
push:
branches: [main]
pull_request:
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app
ports: ["5432:5432"]
options: >-
--health-cmd "pg_isready" --health-interval 10s
--health-timeout 5s --health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/app?sslmode=disable
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
cache: true
- run: go vet ./...
- run: go test ./...
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- run: |
docker build -t ${{ steps.ecr.outputs.registry }}/gin-api:${{ github.sha }} .
docker push ${{ steps.ecr.outputs.registry }}/gin-api:${{ github.sha }}
- uses: aws-actions/amazon-ecs-render-task-definition@v1
id: taskdef
with:
task-definition: taskdef.json
container-name: gin-api
image: ${{ steps.ecr.outputs.registry }}/gin-api:${{ github.sha }}
- uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.taskdef.outputs.task-definition }}
service: gin-api
cluster: prod
wait-for-service-stability: trueCaching and speed
setup-go with cache: true restores the build and module cache. Add docker/build-push-action with GitHub Actions cache (cache-from/cache-to: gha) to reuse image layers. Image builds and ECS rollouts are slow; cheaper managed runners such as Latchkey (around 69% cheaper than GitHub-hosted) cut build time and auto-retry a transient ECR push failure.
Deploying
Use OIDC role assumption with ECR push and ECS deploy permissions. Tag images with the commit SHA for traceable rollbacks. wait-for-service-stability blocks until the new tasks are healthy so a failed deploy fails the workflow.
Key takeaways
- Back Gin integration tests with a Postgres service container.
- Tag images with the commit SHA so rollbacks are unambiguous.
- Wait for ECS service stability so failed rollouts fail CI.