Staging auto-syncs. But production needs a human decision. The GitOps way: a Pull Request in the GitOps repo IS the approval. Merging it IS the deployment. This gives you audit trail, review, and one-click rollback (revert the PR).

The Promotion Flow

CI builds image sha-abc1234 Staging: auto-sync CI commits tag directly Verify staging smoke tests pass CI creates PR: "Promote sha-abc to prod" Updates environments/production/ 👤 Team reviews PR Sees exactly what's changing Merges → ArgoCD syncs prod ✅ Rollback = Revert the PR git revert → ArgoCD syncs old tag → instant rollback Full audit trail in Git history
PR merge = production deployment. PR revert = production rollback. Git history = complete audit trail.
The PR IS the approval gate. No need for ArgoCD manual sync buttons or GitHub Environment approvals. The code review process you already have becomes your deployment approval process.

🏋️ CI Workflow: Auto-Promote to Staging, PR for Production

Add a promotion job to your CI pipeline (in the app repo):

  promote-to-production:
    name: 🚀 Create Production PR
    needs: [build, update-gitops]   # After staging is deployed
    runs-on: ubuntu-latest
    steps:
      - name: Checkout GitOps repo
        uses: actions/checkout@v4
        with:
          repository: ${{ env.GITOPS_REPO }}
          token: ${{ secrets.GITOPS_PAT }}

      - name: Update production image tag
        run: |
          cd environments/production
          sed -i "s/newTag: .*/newTag: ${{ needs.build.outputs.tag }}/" kustomization.yaml

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ secrets.GITOPS_PAT }}
          commit-message: "promote: ${{ needs.build.outputs.tag }} → production"
          title: "🚀 Deploy ${{ needs.build.outputs.tag }} to Production"
          body: |
            ## Production Deployment
            **Image:** `${{ needs.build.outputs.tag }}`
            **Staging:** verified ✅
            **Source commit:** ${{ github.sha }}
            ---
            Merge this PR to deploy to production.
            Revert this PR to rollback.
          branch: promote/${{ needs.build.outputs.tag }}
          labels: deployment, production

What the Reviewer Sees

🚀 Deploy sha-abc1234 to Production +1 -1 environments/production/kustomization.yaml - newTag: sha-old7890 + newTag: sha-abc1234
The reviewer sees EXACTLY what's changing: one line, one image tag. Clear, auditable, reversible.

Production ArgoCD Config (Manual Sync)

# apps/production.yaml — no automated sync!
spec:
  syncPolicy:
    # No 'automated' key = manual sync only
    syncOptions:
      - CreateNamespace=true
    # But still self-heal (prevent drift)

ArgoCD detects the change when the PR is merged → shows "OutOfSync" → operator clicks Sync (or you enable auto-sync on production if the PR review IS your approval gate).

Architect decision: Two valid approaches for production:
  1. Manual ArgoCD sync — PR merge + ArgoCD manual sync (double approval)
  2. Auto-sync on production — PR merge IS the deployment (the review was the gate)
Most mature teams use option 2: the PR review process is rigorous enough that merging = approved to deploy.

🧠 Recall Check

  1. In the PR-based promotion flow, what triggers the actual production deployment?
  2. How do you rollback a production deployment in this model?
  3. Why is a PR better than a "manual sync" button in ArgoCD?
  4. What does the staging→production promotion flow look like end-to-end?
Reveal answers
  1. Merging the PR in the GitOps repo. This updates the production kustomization.yaml with the new image tag. ArgoCD detects the change and syncs.
  2. Revert the PR. Git creates a revert commit (changes tag back to previous value) → ArgoCD syncs → old image is deployed. Complete audit trail preserved.
  3. PRs give you: code review (someone looks at what's deploying), discussion threads, audit trail (who approved, when), easy rollback (revert), and integration with branch protection rules.
  4. Push code → CI builds image → CI commits tag to staging (direct commit) → ArgoCD auto-syncs staging → staging verified → CI creates PR for production → team reviews → PR merged → ArgoCD syncs production.
GitOps promotion complete. You now have a full production-grade GitOps delivery system: CI → staging (auto) → verified → PR → production (reviewed). Next section: hardening everything with security best practices.