Full GitOps Pipeline — From Code Commit to Production

Why a Full GitOps Pipeline Matters

A complete GitOps pipeline automates the entire software delivery process — from code commit to production deployment — using Git as the single source of truth. This chapter combines GitHub Actions (CI) with ArgoCD (CD) to build a production-ready GitOps workflow with multi-environment promotion and disaster recovery.

Why this matters for your career:

  • End-to-end GitOps pipelines are the industry standard for Kubernetes deployments
  • Combining CI with GitOps CD is the modern DevOps pattern used by leading tech companies
  • Multi-environment promotion with approval gates is essential for enterprise deployments
  • Disaster recovery with GitOps is a key capability that sets GitOps apart from traditional CI/CD

Pipeline Architecture

The pipeline has two main stages:

CI (GitHub Actions): Build → Test → Push Docker image → Update config repo CD (ArgoCD): Detect config change → Sync cluster → Verify health → Promote

Developer → Code Commit → GitHub Actions (CI) → Push Image
                                                    ↓
                                          Update Config Repo (Git)
                                                    ↓
                                          ArgoCD detects change (CD)
                                                    ↓
                                          Sync Dev → Staging → Production

CI Pipeline (GitHub Actions)

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test
      - run: npm run lint

  build-and-push:
    needs: [test]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

      - name: Update image tag in config repo
        run: |
          git clone https://x-access-token:${{ secrets.CONFIG_PAT }}@github.com/myorg/myapp-config.git
          cd myapp-config
          cd k8s/overlays/production
          kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          git config user.name "CI Bot"
          git config user.email "ci-bot@example.com"
          git add .
          git commit -m "Update image tag to ${{ github.sha }}"
          git push

CD Pipeline (ArgoCD ApplicationSet)

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-app
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - env: dev
            namespace: my-app-dev
            path: k8s/overlays/dev
          - env: staging
            namespace: my-app-staging
            path: k8s/overlays/staging
          - env: production
            namespace: my-app-prod
            path: k8s/overlays/production

  template:
    metadata:
      name: 'my-app-{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/myapp-config.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

Multi-Environment Promotion

Strategy

| Environment | Trigger | Approval Required | Rollback Method | |-------------|---------|-----------------|-----------------| | Dev | Auto on merge | None | Revert commit | | Staging | After dev syncs + tests pass | Automated | Revert commit | | Production | Manual approval | Team lead + QA | Revert commit |

Promote via ArgoCD CLI

# Promote staging to production
argocd app sync my-app-production --revision main

# Check sync status
argocd app get my-app-production

# Rollback if needed
argocd app rollback my-app-production --prune

Image Updater Integration

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app-image-updater
  namespace: argocd
  annotations:
    argocd-image-updater.argoproj.io/image-list: my-app=ghcr.io/myorg/my-app
    argocd-image-updater.argoproj.io/my-app.update-strategy: semver
    argocd-image-updater.argoproj.io/my-app.allow-tags: regexp:^v\\d+\\.\\d+\\.\\d+$
    argocd-image-updater.argoproj.io/write-back-method: git:secret:argocd/image-updater-creds
    argocd-image-updater.argoproj.io/git-branch: main
spec:
  source:
    repoURL: https://github.com/myorg/myapp-config.git
    targetRevision: main
    path: k8s/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app-prod

Disaster Recovery

# New cluster provisioned → Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Apply the ApplicationSet
kubectl apply -f applicationset.yaml

# ArgoCD automatically syncs all apps from Git
# Entire cluster restored to desired state in minutes

This reduces recovery time from hours/days to 5-15 minutes.

Monitoring and Alerting

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: my-app
  namespace: argocd
spec:
  sourceRepos:
    - 'https://github.com/myorg/*'
  destinations:
    - namespace: '*'
      server: https://kubernetes.default.svc
  syncWindows:
    - kind: deny
      schedule: '0 22 * * *'
      duration: 6h
      namespaces:
        - my-app-prod
      applications:
        - '*-production'

Best Practices

| Practice | Reason | |----------|--------| | Separate app code from config repo | Security — config repo has narrower access permissions | | Use ApplicationSet for multi-env | DRY — one template generates all environments | | Enable self-heal and prune | Automatic drift correction and cleanup | | Use sync windows for production | Prevent deployments during off-hours | | Add cluster resource whitelist | Limit what ArgoCD can create/modify | | Always use PRs for config changes | Code review for infrastructure changes | | Monitor sync status and send alerts | Detect out-of-sync applications quickly | | Tag images with commit SHA (not latest) | Traceable, reproducible deployments |

Summary

A complete GitOps pipeline combines GitHub Actions for CI (build, test, push) with ArgoCD for CD (auto-sync from Git). Image updater automates version management. ApplicationSet handles multi-environment deployment with a single template. Disaster recovery becomes push-button.

Key takeaways:

  • CI pipeline builds images and updates the config repo
  • ArgoCD detects config changes and syncs the cluster
  • Separate app code from config repo for security
  • ApplicationSet: one template for all environments
  • Image updater auto-detects new versions
  • Promote: dev (auto) → staging (tests) → production (manual approval)
  • Sync windows protect production during off-hours
  • Disaster recovery: new cluster → install ArgoCD → sync from Git
  • Monitor sync status and set up alerts

What's Next: DevOps — Monitoring

The next course covers DevOps monitoring — Prometheus, Grafana, Loki, and OpenTelemetry for comprehensive observability.

Unlock Full Tutorial

This chapter is paid content. Join the project to unlock over 5000 words of deep analysis, including 10+ god-tier Prompts and real Source Code examples!