GitOps Workflow Patterns: Infrastructure as Pull Requests
Practical patterns for implementing GitOps workflows that actually work in production.
February 22, 2026 · 6 min · 1250 words · Rob Washington
Table of Contents
GitOps sounds simple: put your infrastructure in Git, let a controller sync it to your cluster. In practice, there are a dozen ways to get it wrong. Here’s what works.
Git is the source of truth. Not the cluster. Not a dashboard. Not someone’s kubectl session.
If the cluster state doesn’t match Git, the controller fixes it. If someone manually changes the cluster, the controller reverts it. This is the contract.
Managing hundreds of applications individually doesn’t scale. Use a hierarchy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# apps/root.yaml - the "app of apps"apiVersion:argoproj.io/v1alpha1kind:Applicationmetadata:name:rootnamespace:argocdspec:project:defaultsource:repoURL:https://github.com/org/infrastructurepath:appstargetRevision:maindestination:server:https://kubernetes.default.svcnamespace:argocdsyncPolicy:automated:prune:trueselfHeal:true
The root application watches the apps/ directory. Each file in that directory defines another application. Add a new service? Add a YAML file. Delete a service? Delete the file.
# Flux ImageUpdateAutomationapiVersion:image.toolkit.fluxcd.io/v1beta1kind:ImageUpdateAutomationmetadata:name:api-image-updatespec:interval:5msourceRef:kind:GitRepositoryname:infrastructuregit:checkout:ref:branch:maincommit:author:email:flux@example.comname:FluxmessageTemplate: 'chore:update {{.Changed.Name}} to {{.Changed.NewTag}}'push:branch:mainupdate:path:./deploystrategy:Setters
1
2
3
4
5
# In your deploymentspec:containers:- name:apiimage:registry.example.com/api:v1.2.3# {"$imagepolicy": "flux-system:api"}
When a new image is pushed to the registry, Flux updates the tag in Git and commits. The cluster then syncs to the new commit. Full audit trail, no manual steps.
# Prometheus alert- alert:ArgoCDAppOutOfSyncexpr:argocd_app_info{sync_status!="Synced"} == 1for:15mlabels:severity:warningannotations:summary:"Application {{ $labels.name }} is out of sync"
This is useful for stateful applications where automatic reconciliation could cause outages.