GitOps Explained: A Practical Guide for Sysadmins

If you’re managing infrastructure across multiple environments and still using SSH to manually apply changes, it’s time to rethink your approach. A GitOps guide for sysadmins isn’t about replacing your skills—it’s about scaling your impact through version control and automation. In this comprehensive guide, we’ll walk through what GitOps actually means, why it matters for your infrastructure, and how to implement it without breaking your existing workflows.

GitOps has become the de facto standard for infrastructure management in cloud-native environments, and for good reason. But before you start migrating everything to Git, you need to understand the principles, the tools, and—most importantly—how to avoid the common pitfalls that trap teams in perpetual firefighting mode.

What Is GitOps, Really?

GitOps is a set of practices that use Git as the single source of truth for infrastructure and application deployments. Instead of manually SSH-ing into servers or running ad-hoc Terraform commands, you commit your infrastructure changes to a Git repository, and automated tooling ensures your systems match that desired state.

The core principle is simple: Git becomes your deployment mechanism. Your infrastructure configuration, application manifests, and policy definitions all live in version control, and a reconciliation engine continuously ensures your live environment matches what’s declared in Git.

Here’s what makes GitOps different from traditional Infrastructure as Code (IaC):

  • Declarative, not imperative — You describe what the desired state should be, not how to achieve it
  • Version-controlled everything — Every change has a commit history, author, and timestamp
  • Automated reconciliation — Tools continuously verify that reality matches your Git declarations
  • Audit trail by default — Who changed what, when, and why? Check Git history
  • Easy rollbacks — Reverting a deployment is just reverting a commit

It sounds straightforward in theory. In practice, the implementation details determine whether GitOps saves you hours each week or introduces new complexity that frustrates your team.

The GitOps Workflow: How It Actually Works

Let’s walk through a realistic scenario. You’re managing a Kubernetes cluster and need to deploy a new version of your payment service.

Traditional workflow (non-GitOps):
1. Dev pushes new code to your container registry
2. You SSH into the Kubernetes cluster
3. You manually update the deployment YAML
4. You run kubectl apply -f payment-service.yaml
5. You verify the pods are running
6. You document this change in a Slack message or ticket

GitOps workflow:
1. Dev pushes new code to the container registry
2. An automation tool detects the new image
3. It automatically creates a pull request updating the manifest in your Git repo
4. You review and approve the PR
5. The GitOps operator applies the manifest automatically
6. Everything is logged in Git history

The second approach is auditable, reversible, and doesn’t require you to be awake at 2 AM to apply a critical patch.

The Pull-Based Model

Most modern GitOps tools use a “pull-based” approach rather than push-based CI/CD pipelines. Here’s why this matters:

In a push-based model (traditional CI/CD):
– Your CI pipeline has credentials to deploy to production
– If the pipeline is compromised, your production is compromised
– The pipeline must have network access to all your environments

In a pull-based model (GitOps):
– A reconciliation agent runs inside your cluster/infrastructure
– It pulls the desired state from Git and applies it
– Your CI system only needs to push to the container registry
– The agent only needs read access to your Git repository
– You have fewer secrets scattered across your infrastructure

This architectural difference is why GitOps is considered more secure than traditional CI/CD for infrastructure deployments.

Key GitOps Tools for Infrastructure Management

Flux CD

Flux is a lightweight GitOps operator designed specifically for Kubernetes. It’s CNCF-incubated and excellent for teams wanting a minimal, declarative approach.

Strengths:
– Extremely lightweight (runs in a few hundred MB)
– Excellent documentation for Kubernetes-native workflows
– Built-in support for Helm, Kustomize, and plain YAML
– Strong focus on multi-tenancy and security

Install Flux on a Kubernetes cluster:

curl -s https://fluxcd.io/install.sh | sudo bash
flux bootstrap github \
  --owner=YOUR_GITHUB_USERNAME \
  --repo=fleet-infra \
  --path=clusters/production \
  --personal

This command creates a Git repository and installs Flux, which will then manage deployments based on what’s in that repo.

ArgoCD

ArgoCD is more feature-rich and provides a web UI for managing deployments. It’s better suited for teams managing multiple clusters or complex deployment workflows.

Strengths:
– Web UI for monitoring and managing deployments
– Excellent multi-cluster management
– Deep Helm integration
– Sync policies and pruning options

Installing ArgoCD:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

After installation, you’d create an Application manifest:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payment-service
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/infrastructure
    targetRevision: main
    path: apps/payment-service
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

ArgoCD would continuously ensure that what’s in the apps/payment-service directory matches the production cluster.

Terraform Cloud / Terraform Enterprise

If you’re primarily using Terraform for infrastructure, these platforms provide GitOps-style workflows without requiring a separate reconciliation engine.

When you commit Terraform to your Git repository, Terraform Cloud automatically:
– Runs terraform plan
– Waits for approval
– Runs terraform apply

This gives you many GitOps benefits without additional tooling.

Building Your GitOps Repository Structure

The structure of your Git repository dramatically affects how maintainable your infrastructure becomes. Here’s a practical layout for a team managing multiple environments:

infrastructure/
├── clusters/
│   ├── production/
│   │   ├── kustomization.yaml
│   │   ├── flux-system/
│   │   └── apps/
│   │       ├── payment-service/
│   │       ├── api-gateway/
│   │       └── monitoring/
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   └── apps/
│   └── development/
├── apps/
│   ├── payment-service/
│   │   ├── base/
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays/
│   │       ├── production/
│   │       ├── staging/
│   │       └── development/
├── infrastructure/
│   ├── networking/
│   ├── storage/
│   └── monitoring/
└── docs/
    └── DEPLOYMENT_GUIDE.md

This structure uses Kustomize, which lets you maintain a single base application configuration while overlaying environment-specific changes. For example, production might require more replicas, different resource limits, or additional security policies.

Example Kustomize overlay for production:

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
replicas:
- name: payment-service
  count: 5
commonLabels:
  environment: production
patches:
- target:
    kind: Deployment
    name: payment-service
  patch: |-
    - op: replace
      path: /spec/template/spec/containers/0/resources
      value:
        requests:
          memory: "512Mi"
          cpu: "500m"
        limits:
          memory: "1Gi"
          cpu: "1000m"

This approach keeps your base configuration DRY while allowing each environment to diverge slightly.

Implementing GitOps: A Step-by-Step Approach

Phase 1: Start Small and Pilot

Don’t migrate your entire infrastructure to GitOps in one sprint. Pick a single, non-critical application or service.

  1. Choose your tool — For Kubernetes-native teams, start with Flux or ArgoCD. For mixed environments, consider Terraform Cloud.

  2. Create your repository structure — Set up a basic directory layout and initialize it with your first application’s manifests.

  3. Deploy the GitOps operator — Install Flux, ArgoCD, or your chosen tool into a staging cluster.

  4. Deploy one application — Push your first application manifests to Git and watch the operator deploy them.

Example with Flux:

# Install Flux
flux bootstrap github \
  --owner=myorg \
  --repo=fleet-infra \
  --path=clusters/staging

# Create a deployment manifest
mkdir -p clusters/staging/apps/web-app
cat > clusters/staging/apps/web-app/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: myregistry.azurecr.io/web-app:v1.0.0
        ports:
        - containerPort: 8080
EOF

# Create a Flux source and kustomization
flux create source git web-app-repo \
  --url=https://github.com/myorg/infrastructure \
  --branch=main

flux create kustomization web-app \
  --source=web-app-repo \
  --path=clusters/staging/apps/web-app

Phase 2: Establish Workflow Patterns

Once your pilot is successful, establish patterns for your team:

  • Who can commit to main? — Require pull requests and reviews for infrastructure changes.
  • Testing and validation — Add CI checks that validate your manifests before merging.
  • Promotion paths — Define how changes flow from development → staging → production.

Example GitHub Actions workflow for validating manifests:

name: Validate Manifests
on:
  pull_request:
    paths:
      - 'clusters/**'
      - 'apps/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install kustomize
        run: |
          curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
          sudo mv kustomize /usr/local/bin/

      - name: Validate Kubernetes manifests
        run: |
          kustomize build clusters/production > /tmp/manifests.yaml
          kubectl apply --dry-run=client -f /tmp/manifests.yaml

      - name: Check image tags
        run: |
          # Fail if any image uses 'latest' tag
          if grep -r "image:.*:latest" clusters/; then
            echo "❌ Found images with 'latest' tag. Use explicit versions."
            exit 1
          fi

Phase 3: Automate Image Updates

GitOps shines when you automate image updates. After your container image is built and pushed, automatically update your manifests.

Using Flux Image Automation:

apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
  name: web-app
  namespace: flux-system
spec:
  image: myregistry.azurecr.io/web-app
  interval: 1m
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
  name: web-app
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: web-app
  policy:
    semver:
      range: '>=1.0.0 <2.0.0'
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: web-app
  namespace: flux-system
spec:
  interval: 1m
  sourceRef:
    kind: GitRepository
    name: infrastructure
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: [email protected]
        name: Flux
  update:
    path: ./clusters/production
    strategy: Setters

This configuration automatically updates your deployment manifests when a new image is pushed to your registry.

Common GitOps Pitfalls and How to Avoid Them

Pitfall 1: Treating Git as the Ultimate Truth, But Forgetting About Reality

Your Git repository should be the source of truth, but you need to actively verify that your live environment actually matches it.

Prevention: Use drift detection tools:
– Flux has built-in drift detection
– ArgoCD shows drift in the UI
– Set up periodic syncs that correct drift automatically

Pitfall 2: Making Git Too Complex

Teams often over-engineer their GitOps repository structure, creating layers of Helm charts, Kustomize bases, and custom tooling that only one person understands.

Prevention: Keep it simple:
– Start with plain YAML or Kustomize overlays
– Add complexity only when needed
– Document your conventions clearly

Pitfall 3: Secrets in Git

Your GitOps repository should be public (for transparency) or internal, but never containing secrets like database passwords or API keys.

Prevention: Use a secrets management tool:
Sealed Secrets (Kubernetes-native, integrates with Flux)
SOPS (Mozilla’s Secrets Operations tool)
External Secrets Operator (integrates with AWS Secrets Manager, HashiCorp Vault, etc.)

Example with Sealed Secrets:

# Install sealed-secrets
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.18.0/controller.yaml

# Seal a secret
echo -n "mypassword" | kubectl create secret generic db-secret \
  --dry-run=client \
  --from-file=password=/dev/stdin \
  -o yaml | \
  kubeseal -f - > sealed-secret.yaml

# Commit sealed-secret.yaml to Git
# Only your cluster's Sealed Secrets controller can decrypt it

Pitfall 4: Manual Changes to Live Systems

The entire GitOps model breaks if someone SSHes into a server and makes changes. You need governance to prevent this.

Prevention:
– Use RBAC to restrict direct cluster access
– Require all infrastructure changes to go through Git
– Audit and alert on manual changes that bypass GitOps
– Make it easier to commit to Git than to manually change systems

GitOps for Non-Kubernetes Environments

GitOps isn’t just for Kubernetes. You can apply the same principles to VMs, cloud infrastructure, and on-premises systems.

For infrastructure managed with Terraform:
– Use Terraform Cloud with VCS integration
– Commit your .tf files to Git
– Require pull requests for approval
– Terraform Cloud automatically plans and applies changes

For VMs and on-premises:
– Use configuration management tools (Ansible, Puppet, Chef) triggered by Git webhooks
– Store your playbooks and configurations in Git
– Use similar approval workflows

Example Ansible approach:

# In your CI/CD pipeline triggered by Git pushes
---
- name: Apply infrastructure configuration
  hosts: all
  vars:
    git_repo: "https://github.com/myorg/infrastructure"
    git_version: "{{ lookup('env', 'GIT_COMMIT') }}"

  tasks:
    - name: Clone infrastructure repo
      git:
        repo: "{{ git_repo }}"
        dest: /opt/infrastructure
        version: "{{ git_version }}"

    - name: Apply configurations
      include_role:
        name: "{{ item }}"
      loop:
        - networking
        - monitoring
        - security

Comparing GitOps Tools: Quick Reference

ToolBest ForLearning CurveWeb UIMulti-Cluster
Flux CDKubernetes-native, minimal teamsLowCLI-firstGood
ArgoCDComplex deployments, UI preferenceMediumExcellentExcellent
Terraform CloudInfrastructure-heavy environmentsLowGoodN/A
GitLab CI/CDGitLab shops wanting built-in solutionLowBuilt-inGood
GitHub ActionsSimple workflows, GitHub-nativeLowLimitedPossible

Moving Your Sysadmin Workflows to GitOps

If you’ve spent years managing infrastructure manually, GitOps might feel foreign. Here’s how to transition:

  1. Your version control skills translate directly — You probably already use Git. GitOps just extends that to infrastructure.

  2. Your troubleshooting skills are still crucial — GitOps is a deployment tool, not a debugging tool. When things break, you’ll still need to SSH into systems, check logs, and investigate.

  3. You’re not being replaced — GitOps automation handles routine changes. Your expertise matters more for designing systems, preventing outages, and handling edge cases.

  4. Start documenting infrastructure as code — Every manual configuration you have should be captured in your Git repository.

Practical Next Steps

  1. Choose your first tool — For Kubernetes teams, start with Flux (lightweight) or ArgoCD (feature-rich). Both have excellent documentation.

  2. Set up a pilot — Pick one non-critical service and manage it with GitOps for a month.

  3. Build your repository — Create the directory structure we outlined and commit your first application manifests.

  4. Establish approval workflows — Use GitHub’s branch protection rules to require reviews before changes are deployed.

  5. Iterate and expand — Once the pilot is successful, gradually migrate more services and eventually entire environments.

The shift to GitOps isn’t about adopting the latest trendy technology—it’s about making your infrastructure auditable, reversible, and scalable. Once you experience the peace of mind that comes from having a complete, version-controlled history of every infrastructure change, you’ll wonder why you ever managed systems any other way.


Affiliate Disclosure: This article may contain affiliate links. If you purchase through these links, TechChimney may earn a commission at no extra cost to you. We only recommend products we believe provide genuine value.