Create a Production‑Grade GitOps CI/CD Pipeline Using GitHub Actions and Argo
This guide walks through a production‑level GitOps CI/CD pipeline that integrates GitHub Actions for building and pushing Docker images, a separate GitOps repository for declarative Kubernetes manifests managed with Helm and Kustomize, and Argo CD plus Argo Rollouts to deliver automated, safe, progressive releases across staging and production environments.
Overview
This article demonstrates a production‑ready GitOps CI/CD pipeline built with GitHub Actions, Argo CD, and Argo Rollouts. It automates Docker image builds, stores declarative Kubernetes manifests in a separate GitOps repository, and enables safe progressive releases.
End‑to‑End Flow
Code change : Developer opens a pull request and merges to main.
CI (Continuous Integration) : GitHub Actions builds a Docker image and pushes it to GitHub Container Registry (GHCR).
GitOps update : The CI workflow writes the new image tag into the GitOps repository.
CD (Continuous Delivery) : Argo CD detects the change and synchronises the Kubernetes cluster.
Progressive release : Argo Rollouts performs a Blue‑Green or Canary rollout of the new version.
Architecture Principle
The application code repository and the GitOps configuration repository are kept strictly separate.
Application repo ( service-foundry-web): contains source code (React, Go, Dockerfile, etc.).
GitOps repo ( service-foundry-argocd): stores Helm/Kustomize manifests that define how the app runs.
Example Application
The tutorial uses a simple containerised React app available at https://github.com/nsalexamy/service-foundry-web. It serves as a testbed for the full CI/CD chain.
Part 1 – CI with GitHub Actions
Create .github/workflows/build-and-push.yaml in the application repository:
name: Build and Push Image
on:
push:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: nsalexamy/service-foundry-web
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract image tag
id: meta
run: |
TAG=${GITHUB_SHA::7}
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: |
ghcr.io/nsalexamy/service-foundry-web:${{ steps.meta.outputs.tag }}
ghcr.io/nsalexamy/service-foundry-web:latestPart 2 – Build GitOps Config with Helm & Kustomize
Initialize directory
mkdir service-foundry-web-gitops
cd service-foundry-web-gitopsGenerate Helm chart (including Argo Rollout resources)
# Ensure you are in service-foundry-web-gitops
yo open-nsa2:helm-chart-rollouts chart-home/service-foundry-web \
--image-repository ghcr.io/nsalexamy/service-foundry-web \
--image-tag latest \
--service-port 80The command creates chart-home/service-foundry-web with key files: rollout.yaml: Argo Rollout definition. service.yaml: Stable traffic routing. service-preview.yaml: Preview (canary) traffic routing.
Staging environment configuration (Kustomize)
staging/kustomization.yaml :
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
resources:
- ingressroute.yaml
helmGlobals:
chartHome: ../chart-home
helmCharts:
- name: service-foundry-web
releaseName: service-foundry-web
namespace: staging
valuesFile: values-staging.yamlstaging/values-staging.yaml (the tag field will be overwritten by CI):
replicaCount: 4
image:
tag: "latest"
rollouts:
enabled: true
strategy:
blueGreen:
activeService: "service-foundry-web"
previewService: "service-foundry-web-preview"
autoPromotion: false # manual promotion in the demostaging/ingressroute.yaml defines public routes:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: service-foundry-web-staging-ingressroute
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`web-staging.servicefoundry.org`)
kind: Rule
services:
- name: service-foundry-web
port: 80
- match: Host(`web-staging-preview.servicefoundry.org`)
kind: Rule
services:
- name: service-foundry-web-preview
port: 80Part 3 – CD with Argo CD
Create an Argo CD Application that watches the GitOps directory:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: service-foundry-web-staging
namespace: argocd
spec:
project: service-foundry
source:
repoURL: [email protected]:nsalexamy/service-foundry-argocd.git
targetRevision: HEAD
path: demo-apps/service-foundry-web-gitops/staging
destination:
server: https://kubernetes.default.svc
namespace: staging
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueApply the manifest to the cluster:
kubectl apply -f service-foundry-web-staging-app.yamlPart 4 – Auto‑update GitOps Repository
Add a second workflow in the application repository that, after a successful image build, writes the new tag back to the GitOps repo:
name: Update GitOps Repo
on:
workflow_run:
workflows: ["Build and Push Image"]
types:
- completed
jobs:
update:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout GitOps repo
uses: actions/checkout@v4
with:
repository: nsalexamy/service-foundry-argocd
token: ${{ secrets.GITOPS_TOKEN }}
- name: Update image tag
run: |
TAG=${GITHUB_SHA::7}
sed -i "s|tag:.*|tag: \"${TAG}\"|" demo-apps/service-foundry-web-gitops/staging/values-staging.yaml
- name: Commit and push
run: |
git config user.name "github-actions"
git config user.email "[email protected]"
git commit -am "deploy: service-foundry-web ${GITHUB_SHA::7}"
git pushVerification – Run the Full Pipeline
Visit the current version at https://web-staging.servicefoundry.org (e.g., orange theme).
Modify public/config.json in the application repo, changing primaryColor to #673AB7 (purple).
Commit and push to main:
git add .
git commit -m "Update theme color to Deep Purple"
git push origin mainObserve the sequence:
GitHub runs the Build and Push workflow.
GitHub runs the Update GitOps Repo workflow.
Argo CD detects the new tag in values-staging.yaml and synchronises the cluster.
Argo Rollouts launches the preview (green) stack showing the purple version while the main (blue) stack remains orange.
Check the preview URL https://web-staging-preview.servicefoundry.org – it should display the purple theme. If manual promotion is enabled, clicking Promote in the Argo Rollouts UI switches all traffic to the new version.
Summary
Developers focus on business code in the application repository.
GitHub Actions builds Docker images and writes the new tag back to the GitOps repository.
Argo CD continuously reconciles cluster state with the declarative GitOps configuration.
Argo Rollouts provides safe, progressive releases (Blue‑Green or Canary).
The architecture scales from small teams to large‑enterprise environments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
