Back to posts
Post

GitOps Infrastructure Management: Eliminate Manual Deployments

GitOps puts your infrastructure under version control so every change is tracked, reviewed, and deployed automatically. Cut human error and manual SSH sessions.

DevopsDevOpsGitOpsArgo CDFluxInfrastructure as CodeKubernetesTerraform

GitOps infrastructure management means storing your entire infrastructure configuration in Git and letting an automated controller apply those changes to your environment. Instead of SSH-ing into servers and running commands by hand, you push a commit and the system reconciles itself. I've been running this model across Proxmox clusters and Kubernetes workloads for a while now, and the difference in error rates is dramatic.

As I mentioned before in my DevOps vs classic sysadmin post (https://furkanikkan.com/urun/devops-vs-klasik-sysadmin-2025-te-hangi-model-one-cikiyor-56), the shift from manual operations to declarative infrastructure isn't just a trend — it's how you survive running dozens of environments without burning out.

What GitOps Actually Means in Practice

The core idea is simple: Git is your single source of truth. Nothing changes in production unless there's a commit behind it. A controller running inside your cluster continuously compares the desired state in Git with the actual state on the servers, and drifts get corrected automatically.

The four principles I stick to:

  • Declarative configuration — everything is described as code, not imperative scripts
  • Version control — every change has a history, an author, and a rollback point
  • Automatic pull — the controller fetches and applies changes, not a push from CI
  • Continuous reconciliation — if someone manually edits a server, GitOps reverts it

That last point matters more than people think. In my environment, the biggest source of incidents used to be "someone fixed something at 2 AM and forgot to document it." GitOps makes that impossible to hide.

Argo CD and Flux: The Two Tools I Actually Use

For Kubernetes environments, I rely on Argo CD and Flux. Both do the same fundamental job — watch a Git repo, detect changes, apply them to the cluster — but they feel different in practice.

Argo CD has a web UI that's genuinely useful for visualizing what's deployed. I use it for environments where multiple teams need visibility. Flux is lighter, more CLI-driven, and fits better when I want minimal overhead on smaller clusters.

Here's a basic Flux setup I use:

flux bootstrap git \
  --url=ssh://[email protected]/infra/cluster-config \
  --branch=main \
  --path=clusters/staging

That single command installs Flux, connects it to the repo, and starts syncing. From that point on, anything I merge to clusters/staging/ gets applied automatically.

For non-Kubernetes infrastructure — Proxmox VMs, DNS records, load balancer configs — I use Terraform with the Git repo as the trigger. A CI pipeline runs terraform plan on every PR, and terraform apply only after merge to main.

Cutting Human Error With Pull Requests

The biggest win isn't the automation — it's the review process. When someone wants to change a firewall rule or bump a replica count, they open a PR. Another pair of eyes sees the diff before anything touches production. I've caught misconfigured CIDR ranges, wrong memory limits, and a DNS TTL set to 0 seconds — all in PR reviews before they caused outages.

The workflow looks like this:

  1. Engineer creates a branch and edits the relevant manifest
  2. CI runs terraform plan or kubectl diff --server-side and posts results to the PR
  3. A reviewer approves or requests changes
  4. Merge to main triggers the controller to apply the change
  5. If something breaks, git revert rolls back to the previous state in seconds

Compare that to the old way: someone SSH-es in, makes a change, something breaks at 3 AM, and nobody knows what changed. I've lived that scenario too many times.

Handling Configuration Drift Without Downtime

Drift happens. Someone tests something on a node, a package gets updated outside the pipeline, or a cron job modifies a config file. Without GitOps, that drift silently accumulates until it causes an incident.

With continuous reconciliation, the controller detects drift and corrects it. If I set replicas to 4 in Git and someone manually scales to 6, within a minute it's back to 4. That sounds rigid, but it's exactly what you want in production. If the change was legitimate, it goes through Git first.

Warning: this means your team needs to understand that manual changes are temporary by design. I had a developer who kept editing ConfigMaps directly and getting confused when they reverted. Training matters — the tooling is only as good as the process around it.

What I'd Recommend Starting With

If you're running Kubernetes, start with Argo CD. The UI helps people understand what's happening, and the learning curve is gentler. For teams already comfortable with CLI workflows, Flux is faster to set up and lighter on resources.

For everything outside Kubernetes — VMs, DNS, CDN configs, firewall rules — put it in Terraform and trigger apply from Git. You don't need a fancy controller for that part; a GitLab CI or GitHub Actions pipeline running on merge works fine.

The initial setup takes a weekend. The payoff is permanent. My incident count dropped significantly after moving to GitOps, and rollback times went from "restore from backup and hope" to a single git revert.

GitOps isn't a silver bullet — you still need monitoring, alerting, and good backups (as I covered in my 3-2-1 backup rule post: https://furkanikkan.com/urun/3-2-1-yedekleme-kurali-sadece-yedek-almak-yetmez-49). But it removes the most common failure mode in infrastructure management: untracked manual changes. If you're still SSH-ing into production servers to make config changes, this is your next move.


Cover image: HD Wallpapers · CC0 (Openverse / kamu malı) · https://stocksnap.io/photo/light-abstract-V9L6XXK3LB