← Back to list

GitOps — Ship Microservices with GitHub Actions and ArgoCD

The problem with traditional CI/CD:-

Shashwat Tripathi in AWS Tip · 2026-07-09 20:41 · 0 claps · 6.0 min read
#gitops #argo-cd #aws #ci-cd-pipeline #github-actions
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🔓 · Open Source

GitOps — Ship Microservices with GitHub Actions and ArgoCD

The problem with traditional CI/CD:-

If you’ve ever shipped microservices to Kubernetes, you’ve probably lived through this workflow (the old way):

  1. Developer pushes code to main.

  2. CI pipeline builds a Docker image and pushes it to a registry.

  3. CI pipeline then kubectl applys the new manifests directly to the cluster.

  4. Something breaks. You roll back by re-running a previous pipeline job, hunting for the old image tag in your CI logs.

There are three challenges hiding in that innocent-looking list:

  • The cluster is mutated from outside:- Your CI tool holds cluster-admin credentials, which is a security bad practice.
  • State lives in the CI tool, not in Git:- The “source of truth” for what’s running in production is a pipeline run, not a commit. Ask “what’s in prod right now?” and you have to query the cluster, not the repo.
  • Rollback is an operation, not a state:- You can’t just git revert, you have to orchestrate a redeploy.

GitOps flips all the above problems:-

- Git is the single source of truth, and an agent inside the cluster continuously reconciles the cluster state to match what’s in Git.

  • Nothing mutates the cluster from outside. ArgoCD (the agent) runs inside the cluster and pulls from Git.
  • “What’s in prod?” == “What’s in main?”. Just look at the repo.
  • Rollback == git revert. ArgoCD picks up the change and reconciles.

The pipeline I am going to create, splits the work in two halves:-

Before provisioning the infra and starting the activity, let’s understand what all components this code with deploy on AWS:-

  • It will deploy, VPC, networking resources, ECR, EKS (this module also has AWS Load balancer controller, ArgoCD deployed via helm)
  • Github OIDC role, so that github action can push the image to ECR repos.
# Let's provision the infra on AWS
# Let me show you how, using a real project: linkshrink, a URL shortener
# build as five Node/TypeScript microservices, deployed to AWS EKS.

git clone sha2121/gh-actions-argocd
cd gh-actions-argocd/tf-infra
terraform init
terraform apply

# Update kubeconfig
aws eks update-kubeconfig --region us-east-1 --name dev-eks-cluster

# Apply the argocd resource
# ArgoCD Application will syncs the web-app Helm chart from this repo
kubectl apply -f argocd/01-applications.yaml

Entire flow at a glance

Entire flow at a glance

  • Now, whenever any new changes are pushed to ‘main’ branch, the Github actions will re-build the image and put a new tag on ‘chart/values.yaml’, which ArgoCD will detect and deploy on EKS.
  • Before triggering the workflow, let’s understand the workflow yaml as well:-
# cat .github/workflows/build-push-app.yaml
name: Build and Push Web App to ECR

on:
  push:
    branches: [ "main" ]
    paths:
      - "app/**"
      #- ".github/workflows/build-push-app.yaml" # During testing phases only for prod comment this line

permissions:
  id-token: write # Needed for OIDC
  contents: write # Needed to push commits

env:
  AWS_REGION: us-east-1
  ECR_REPOSITORY: web-app
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

concurrency:
  group: gitops-push
  cancel-in-progress: false

jobs:
  build-and-push-app:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::311902596413:role/github-actions-oidc-role
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Define image tags
        id: vars
        run: |
          SHA_TAG=sha-${GITHUB_SHA::7}
          IMAGE_BASE="${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}"
          echo "IMAGE_BASE=$IMAGE_BASE" >> $GITHUB_ENV
          echo "TAGS=latest $SHA_TAG" >> $GITHUB_ENV
          echo "Using tags: latest and $SHA_TAG"
          echo "latest = convenience tag for testing"
          echo "$SHA_TAG = immutable tag used in Helm values"

      - name: Build and push Docker images
        run: |
          for tag in $TAGS; do
            IMAGE_URI="$IMAGE_BASE:$tag"
            echo "Building and pushing $IMAGE_URI"
            docker build -t $IMAGE_URI app
            docker push $IMAGE_URI
          done

      - name: Setup Git auth using GITHUB_TOKEN
        run: |
          git config --global user.name "ci-bot"
          git config --global user.email "ci-bot@abc.com"
          git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git

      - name: Update Helm values file with new image tag
        run: |
          cd chart
          git config --global user.name "abc-ci"
          git config --global user.email "ci@abc"
          sed -i "s|^  tag: .*|  tag: sha-${GITHUB_SHA::7}|" values-app.yaml
          git diff --quiet || (
            git add values-app.yaml
            git commit -m "Update app image tag to sha-${GITHUB_SHA::7}"
            git push origin main
          )

      - name: CI Complete
        run: |
          echo "Image push and Git update done"
          echo "Tags used: $TAGS"
  • Triggers on push to main when files under app/** change, using OIDC (id-token: write) so GitHub Actions can assume an AWS IAM role.
  • Builds the app’s Docker image from app/ and pushes two tags to ECR: latest (convenience) and sha-<7chars> (immutable, used by Helm)
  • Commits the new image tag back to chart/values-app.yaml in the same repo, ArgoCD detects this Git change and auto-syncs the rolling update to EKS.
  • Once, entire infra is deployed, make some random manual changes in the index.html file in app directory and push the changes:-
# app/index.html line 39
Ship faster with <span class="grad">GitOps-driven</span> cloud delivery V1 # change to V1

# Commit and push the changes
git add app/index.html
git commit -m "trigger CI"
git push
  • In the actions section of the repository, we can see the workflow being triggered:-

  • Once the workflow has finished execution, the image will be pushed to ECR as follows:-

  • Go to chart/values-app.yaml, the tag is automatically updated by the ci with the new tag:-

  • Wait for few minutes until ArgoCD picks up the changes and deploy this app in EKS:-
# List the pods in default namespace
kubectl get pods

# To check the deployment on Argo UI:-
# Get the admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d ; echo

#  Port-forward the server to localhost:8080
kubectl -n argocd port-forward svc/argocd-server 8080:80

# Then open http://localhost:8080 in your browser and log in with:
- Username: admin
- Password: (the string printed in step 1)

  • In order to access the application on the browser:-
# Port forward the web-app service
kubectl port-forward svc/web-app 8081:80

# On browser
http://localhost:8081

  • Again make the changes to code, in order to see the automated build and deploy in action:-
# app/index.html line 39
# change to V2
Ship faster with <span class="grad">GitOps-driven</span> cloud delivery V2

# Commit and push the changes
git add app/index.html
git commit -m "trigger CI"
git push
  • Again the workflow will trigger automatically, build a new image, push it to ECR and update the tag in values-app.yaml which will be detected by ArgoCD:-
  • The version now deployed is V2

  • In order to revert this, fetch the previous commit ID and revert to it:-
# Check which commit you want to go back to
git log --oneline -10 origin/main

# check for the last deployed image tag and the corresponding commit ID 

# Revert
git revert 7fe3bc3 --no-edit && git push
  • The UI would show the previous build version V1.

Key Takeaways:-

  • Splitting CI and CD is the best decision one can make. GitHub Actions builds the image and writes the tag back to Git whereas ArgoCD deploys it. Neither knows about the other, and that’s exactly why it works.
  • OIDC is the saviour. No AWS_ACCESS_KEY_ID secret in the repo, no rotated keys, no leaked credentials in CI logs.
  • No rollback job in CI, no “find the old image tag” , no special procedure.

Follow me for more such technical articles on DevOps

Connect with me on LinkedIn as well.


메타데이터
post_id
a452dc5752d3
slug
gitops-ship-microservices-with-github-actions-and-argocd-a452dc5752d3
url
https://awstip.com/gitops-ship-microservices-with-github-actions-and-argocd-a452dc5752d3
canonical_url
https://awstip.com/gitops-ship-microservices-with-github-actions-and-argocd-a452dc5752d3
author_url
https://medium.com/@shashwattripathi11
status
ok
fetched_at
2026-07-10 13:01:02