Skip to content
Latchkey

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.

.github/workflows/ci.yml
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: true

Caching 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.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →