Almost every incident begins with the same question: what changed? In a lot of organisations the honest answer is that nobody is quite sure, because the change was not a code deploy but a manual tweak, and those leave a much fainter trail. Config-induced failures made up around 9 per cent of classified unplanned incidents in 2025, and they are singled out as harder to detect than deploy regressions precisely because the change-management trail is weaker and more fragmented (StackGen, 2026).
Kubernetes makes this easy to fall into. Teams reach for manual kubectl commands, someone changes something directly in production, and there is no clear record of who changed what, when, or why (Jeevisoft, 2025). GitOps exists, in large part, to make that question always answerable, and to stop unauthorised changes from quietly persisting.
This post is about the accountability side of GitOps specifically: how it turns every production change into an attributable event, how it catches the changes that skip the process, and the honest limits of what it can tell you. No code, just the argument.
The question every incident starts with: what changed?
The reason manual changes are so corrosive is not that they are always wrong, it is that they are invisible. A kubectl edit made during a 2am outage, a value tweaked in a cloud console, an emergency hotfix applied straight to the cluster, each one moves production away from what anyone believes is running, and none of them writes down who did it or why.
That invisibility is exactly what makes config-induced incidents so slow to resolve: the change is often not in the same audit trail as code deploys, so investigators cannot simply look at the deploy history to find the culprit (StackGen, 2026). The team ends up doing archaeology across Slack, shell history, and memory, during the exact window when speed matters most.
The nuance is that this is not a people problem to be solved with more discipline. Under pressure, capable engineers will always reach for the fastest fix, so the answer is not to forbid manual changes by policy alone, but to make the sanctioned path the one that records everything automatically.
With GitOps, the audit trail is the git log
When the only sanctioned way to change production is to change the repository, every change becomes a commit, and a commit already records the author, the time, the reason in the message, and the exact diff from the previous state. You get that audit trail for free, a complete history of who changed what, when, and why, without adding a single logging tool (Pulumi, 2026).
The contrast with manual operations is stark. In regulated environments, an auditor asking to see the change history for a system can simply be pointed at the git log. With kubectl apply, the honest audit trail is that someone ran a command at some point, because kubectl does not record who applied what, or from where (Nik Ogura, 2026).
Drift detection catches the changes that skip the process
An audit trail only covers changes that go through Git, so the obvious gap is the manual change made directly to the cluster. This is what drift detection closes. A GitOps controller continuously compares the live cluster against what Git declares, and any difference is drift, flagged the moment it is noticed. If someone runs a kubectl delete to remove a deployment, the operator detects the missing resource and recreates it from Git within minutes (Atmosly, 2025).
That detection is itself a signal worth watching. Because the cluster is supposed to match Git at all times, any drift event is effectively a notification that someone changed something outside the sanctioned path, which is exactly the out-of-band change you most want to know about.
The nuance is that detection and correction are separate decisions. Many teams begin with detection and alerting, so they know when a manual change happens, and only later turn on automatic reversion once they trust their exceptions, which is a sensible middle ground rather than a compromise (Pulumi, 2026).
Self-healing means unauthorised changes cannot quietly persist
Once correction is enabled, the loop becomes self-healing: manual changes are automatically reverted, unauthorised modifications are quickly detected and corrected, and the cluster converges back to the declared state (BridgePhase, 2026). The useful mental model is a construction supervisor who constantly compares the building against the blueprint and immediately corrects any deviation, so the building can never silently diverge from the plans.
The behavioural effect is as important as the technical one. When engineers learn that a direct change will simply be reverted on the next sync, they stop making them, and the healthy instinct becomes to treat a direct cluster change like touching a hot stove and route the fix through Git instead (Pulumi, 2026).
What it does not solve, and the honest limits
GitOps sharpens accountability, but it is not a complete audit system on its own. Reconciliation happens at intervals, not instantly, so there is a window between syncs where a manual change exists undetected, and anything living in a namespace the controller does not manage is outside its view entirely. The trail is only as complete as the coverage.
There is also a subtle gap in the accountability itself. The controller reliably detects that the live state drifted and reverts it, but it does not always capture who made the manual change, only that the cluster no longer matched Git, so for the full picture you still need the cluster's own audit logs alongside the git history. And self-healing has a sharp edge worth naming: if someone applies a genuine emergency fix by hand and it is not committed, the controller will faithfully revert it, which is why that fix has to be landed in Git rather than left on the cluster (Atmosly, 2025).
The nuance is that these are reasons to adopt it deliberately, not reasons to keep operating by hand. Turning on drift detection first, documenting the deliberate exceptions, and treating self-heal alerts as signals rather than noise gets most of the accountability while you build the confidence to enforce the rest.
The part worth sitting with
So ask the question the next incident will force anyway: if production changed ten minutes ago, could the team say who did it, what they changed, and why, without guessing? For a cluster run by hand the answer is usually no, and that gap is not a tooling detail, it is the difference between an investigation that starts at a git commit and one that starts at a shrug. GitOps closes it by making the only sanctioned way to change production a reviewed, attributed commit, and by having a controller quietly revert anything that tried to skip that path. It will not capture every manual keystroke on its own, and it needs to be rolled out with care rather than flipped on overnight, but it turns the most stressful question in any outage into one with an answer. The change history stops being something a team reconstructs under pressure, and becomes something it can simply read.
Author note
I am Manjunaathaa, an Associate DevOps Engineer at Frigga Cloud Labs. I work across AWS, GCP, and Azure daily, with GitHub Actions as my deployment backbone. My focus is Proactive Resilience, and resilience is not only about recovering quickly, it is about knowing what changed, because you cannot fix fast what you cannot see. Every practice in this post is something I actually run in production, not something I read about. The moment it clicked for me was an incident that traced back to a manual change nobody had recorded, and the hour we lost was spent not fixing the problem but finding out what the problem even was. Now the sanctioned way to touch production is a commit, and my drift alerts tell me the instant someone goes around it. I treat who changed prod as a question that should always have an answer sitting in the git log, never a mystery we solve at 2am. Connect with me on LinkedIn → Manjunaathaa.
DevOps, Security, GitOps, Kubernetes, Drift Detection, Audit Trail, Argo CD, Configuration Drift, Compliance, Self-Healing
