| Storing plain-text secrets in Git | Credentials exposed in repo history forever | Use Sealed Secrets, SOPS, or External Secrets Operator |
| Push-based deploys alongside GitOps | CI kubectl apply bypasses reconciliation | Remove all kubectl apply from CI; only update Git |
| Single repo for app code and manifests | Every code commit triggers deploy; noisy history | Separate application and deployment repositories |
| Branch-per-environment | Branches diverge, merge conflicts, unclear state | Use directory-per-environment with Kustomize overlays |
| Disabling self-heal / auto-prune | Manual changes accumulate, cluster drifts from Git | Enable selfHeal: true and prune: true |
| No health checks in sync policy | Sync marked complete before app is actually ready | Add health checks for Deployments, StatefulSets, Jobs |
Manual kubectl in GitOps clusters | Reverted on next reconciliation or causes drift | All changes through Git PRs; restrict kubectl to read-only |
| Deploying directly to prod | No validation in lower environments | Require promotion path: dev -> staging -> prod |
| No RBAC on GitOps tool | Any developer can sync any app to any namespace | Use ArgoCD AppProjects or Flux multi-tenancy |
Image tag latest in manifests | Non-deterministic deploys, no rollback possible | Use immutable tags (SHA, semver, build ID) |
| No notification on sync failure | Failed deployments go unnoticed for hours | Configure alerts to Slack, PagerDuty, or email |
| Reconciliation interval too long | Drift persists, delayed deployments | Set interval to 1-5 minutes; use webhooks for instant sync |