gitops-patterns

GitOps patterns — ArgoCD and Flux setup, Kustomize overlays per environment, SealedSecrets/ESO for secrets, multi-cluster topology, GitOps repository patterns (mono-repo vs poly-repo), and common pitfalls.

8 stars

Best use case

gitops-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

GitOps patterns — ArgoCD and Flux setup, Kustomize overlays per environment, SealedSecrets/ESO for secrets, multi-cluster topology, GitOps repository patterns (mono-repo vs poly-repo), and common pitfalls.

Teams using gitops-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/gitops-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/gitops-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/gitops-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How gitops-patterns Compares

Feature / Agentgitops-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

GitOps patterns — ArgoCD and Flux setup, Kustomize overlays per environment, SealedSecrets/ESO for secrets, multi-cluster topology, GitOps repository patterns (mono-repo vs poly-repo), and common pitfalls.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# GitOps & Progressive Delivery

GitOps treats Git as the single source of truth for infrastructure and application state. Controllers continuously reconcile the desired state (in Git) with the actual state (in the cluster), eliminating manual `kubectl apply` in production.

## When to Activate

- Setting up ArgoCD or Flux for Kubernetes deployments
- Designing GitOps repository structure (mono-repo vs. config-repo)
- Configuring multi-cluster GitOps with ApplicationSets
- Managing secrets in a GitOps workflow (Sealed Secrets, External Secrets Operator)
- Migrating from imperative `kubectl` workflows to declarative GitOps
- Configuring drift detection and automated remediation

> For canary/blue-green deployments and automated rollback with Argo Rollouts — see skill `progressive-delivery`.

---

## GitOps Core Principles

Four principles from the OpenGitOps standard:

| Principle | Meaning |
|-----------|---------|
| **Declarative** | System state fully described in YAML/Helm/Kustomize — no "scripts that do things" |
| **Versioned & Immutable** | Git history is the audit log; every change is a commit |
| **Pulled Automatically** | Cluster pulls changes from Git (not CI pushes to cluster) |
| **Continuously Reconciled** | Controller loops compare desired vs. actual state, fix drift automatically |

---

## ArgoCD

### Application CRD

```yaml
# apps/my-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: my-team
  source:
    repoURL: https://github.com/myorg/config-repo
    targetRevision: main
    path: apps/my-app/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app-prod
  syncPolicy:
    automated:          # Remove for manual sync in production
      prune: true       # Delete resources no longer in Git
      selfHeal: true    # Revert manual changes automatically
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
  revisionHistoryLimit: 10
```

### Sync Strategies

```yaml
# Manual sync (recommended for production)
syncPolicy: {}   # No automated block — requires manual Sync in UI or CLI

# Automated sync (good for dev/staging)
syncPolicy:
  automated:
    prune: true
    selfHeal: true

# Sync with resource hooks (e.g., run migrations before deploy)
syncPolicy:
  syncOptions:
    - RespectIgnoreDifferences=true
```

### App of Apps Pattern

One root Application manages all other Applications:

```yaml
# root-app.yaml — bootstraps the whole cluster
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  source:
    path: clusters/production/apps   # Directory of Application manifests
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
```

### ApplicationSet — Dynamic App Generation

```yaml
# Generate one Application per directory in apps/
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/config-repo
        revision: main
        directories:
          - path: apps/*/overlays/production
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/config-repo
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
```

### Health Checks

ArgoCD checks resource health automatically. Custom health checks for CRDs:

```lua
-- health-check.lua (in argocd-cm ConfigMap)
hs = {}
if obj.status ~= nil then
  if obj.status.phase == "Running" then
    hs.status = "Healthy"
    hs.message = "Running"
    return hs
  end
  if obj.status.phase == "Failed" then
    hs.status = "Degraded"
    hs.message = obj.status.message
    return hs
  end
end
hs.status = "Progressing"
hs.message = "Waiting for status"
return hs
```

### RBAC: Projects Isolate Teams

```yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: my-team
  namespace: argocd
spec:
  description: My Team applications
  sourceRepos:
    - 'https://github.com/myorg/*'
  destinations:
    - namespace: 'my-team-*'
      server: https://kubernetes.default.svc
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  roles:
    - name: developer
      description: Can sync apps but not delete
      policies:
        - p, proj:my-team:developer, applications, get, my-team/*, allow
        - p, proj:my-team:developer, applications, sync, my-team/*, allow
      groups:
        - my-team-developers
```

### Image Updater

Automatically commit new image tags to Git when a new image is pushed:

```yaml
# Annotation on ArgoCD Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: my-app=myorg/my-app
    argocd-image-updater.argoproj.io/my-app.update-strategy: semver
    argocd-image-updater.argoproj.io/my-app.allow-tags: regexp:^v[0-9]+\.[0-9]+\.[0-9]+$
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/git-branch: main
```

---

## Flux

### Core Controllers

```yaml
# GitRepository — what to watch
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: config-repo
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/config-repo
  ref:
    branch: main
  secretRef:
    name: flux-system   # SSH or HTTPS credentials

---
# Kustomization — what to apply
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  path: ./apps/my-app/overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: config-repo
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: my-app
      namespace: my-app-prod
  postBuild:
    substituteFrom:
      - kind: ConfigMap
        name: cluster-vars
```

### HelmRelease

```yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
  namespace: flux-system
spec:
  interval: 1h
  chart:
    spec:
      chart: cert-manager
      version: '>=1.14.0 <2.0.0'
      sourceRef:
        kind: HelmRepository
        name: jetstack
  values:
    installCRDs: true
    replicaCount: 2
  valuesFrom:
    - kind: ConfigMap
      name: cert-manager-values
```

### Flux Notification Controller

```yaml
# Alert to Slack on sync failure
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: slack-alert
  namespace: flux-system
spec:
  providerRef:
    name: slack
  eventSeverity: error
  eventSources:
    - kind: Kustomization
      name: '*'
    - kind: HelmRelease
      name: '*'
  summary: "Flux sync failure in production cluster"
```

### Flux vs. ArgoCD Decision Guide

| Criteria | Flux | ArgoCD |
|----------|------|--------|
| UI | None (CLI only) | Rich web UI |
| Multi-cluster | Via separate Flux instances | Built-in with ApplicationSet |
| Kubernetes-native feel | Yes (pure CRDs) | Yes but heavier |
| Helm support | Via HelmRelease CRD | Native, with live rendering |
| Complexity | Lower baseline | Higher, more features |
| Best for | Platform teams who prefer GitOps-native | Product teams who want UI |

---

## Kustomize in GitOps

### Repository Structure

```
config-repo/
├── base/
│   └── my-app/
│       ├── deployment.yaml
│       ├── service.yaml
│       └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml      # patches + resources
    │   └── replica-patch.yaml
    ├── staging/
    │   └── kustomization.yaml
    └── production/
        ├── kustomization.yaml
        └── hpa-patch.yaml
```

```yaml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base/my-app
namePrefix: prod-
nameSuffix: ""
commonLabels:
  environment: production
patches:
  - path: hpa-patch.yaml
images:
  - name: myorg/my-app
    newTag: v1.2.3   # Updated by CI or Image Updater
configMapGenerator:
  - name: app-config
    literals:
      - LOG_LEVEL=warn
      - REPLICA_COUNT=5
```

---

## Secrets Management in GitOps

**NEVER** commit plaintext secrets to Git. Choose one:

### Option 1: Sealed Secrets (simple, self-contained)

```bash
# Encrypt a secret for the cluster's public key
kubeseal --fetch-cert --controller-namespace kube-system > pub-cert.pem
kubectl create secret generic my-secret \
  --from-literal=API_KEY=supersecret \
  --dry-run=client -o yaml | \
  kubeseal --cert pub-cert.pem -o yaml > sealed-secret.yaml
# Commit sealed-secret.yaml safely — only the cluster can decrypt
git add sealed-secret.yaml && git commit -m "feat: add sealed API key"
```

### Option 2: External Secrets Operator (ESO) — recommended for production

```yaml
# ExternalSecret — pulls from AWS Secrets Manager, Vault, etc.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-secrets
spec:
  refreshInterval: 5m
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: my-app-secrets          # Creates a native Kubernetes Secret
    creationPolicy: Owner
  data:
    - secretKey: API_KEY           # Key in the K8s Secret
      remoteRef:
        key: prod/my-app           # Path in Secrets Manager
        property: api_key
```

```yaml
# ClusterSecretStore — one per cluster, points to the secret backend
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: eu-west-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets
```

---

## Multi-Cluster GitOps

### Hub-Spoke Architecture

```
Management Cluster (ArgoCD/Flux installed)
  ├── dev-cluster    (ApplicationSet target)
  ├── staging-cluster
  └── prod-cluster
```

```yaml
# ApplicationSet targeting all clusters in ArgoCD
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-app-all-clusters
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            environment: production   # Only prod clusters
  template:
    metadata:
      name: 'my-app-{{name}}'
    spec:
      project: default
      source:
        path: apps/my-app/overlays/production
      destination:
        server: '{{server}}'
        namespace: my-app
```

---

## GitOps Repository Patterns

### Option A: Mono-Repo (small teams)
```
repo/
├── src/           # Application code
├── k8s/
│   ├── base/
│   └── overlays/
└── .github/workflows/
    └── ci.yml     # Build image, update k8s/overlays/*/kustomization.yaml
```

### Option B: Separate Config Repo (recommended for teams)
```
app-repo/          # Source code + CI (builds image, opens PR to config-repo)
config-repo/       # Kubernetes manifests only — GitOps controller watches this
  ├── clusters/
  │   ├── dev/
  │   ├── staging/
  │   └── prod/
  └── apps/
      └── my-app/
          ├── base/
          └── overlays/
```

### CI → GitOps Bridge

```yaml
# In app-repo CI: after successful image build, update config-repo
- name: Update image tag in config-repo
  run: |
    git clone https://github.com/myorg/config-repo
    cd config-repo
    cd apps/my-app/overlays/staging
    kustomize edit set image myorg/my-app=myorg/my-app:${{ github.sha }}
    git config user.email "ci@myorg.com"
    git config user.name "CI Bot"
    git add -A
    git commit -m "chore: update my-app to ${{ github.sha }}"
    git push
```

---

## Common Pitfalls

| Problem | Solution |
|---------|----------|
| Manual `kubectl apply` bypasses GitOps | Enable `selfHeal: true` + RBAC to prevent direct cluster access |
| Secrets in Git | Use ESO or Sealed Secrets — never commit plaintext |
| All environments auto-sync | Production must require manual sync approval |
| No drift detection alerts | Configure ArgoCD Notifications / Flux Alerts |
| Helm values spread across repos | Keep values files in config-repo alongside the HelmRelease |
| Image tag drift | Use ArgoCD Image Updater or Flux Image Automation |

Related Skills

zero-trust-patterns

8
from marvinrichter/clarc

Zero-Trust security patterns — mTLS between microservices (Istio/SPIFFE), SPIRE workload identity, OPA/Envoy authorization, NetworkPolicy default-deny-all, short-lived credentials, service mesh security, and Kubernetes RBAC hardening.

webrtc-patterns

8
from marvinrichter/clarc

WebRTC patterns — peer connection setup, ICE/STUN/TURN configuration, signaling server design, SFU vs mesh topology, screen sharing, media track management, and reconnect/ICE restart handling.

webhook-patterns

8
from marvinrichter/clarc

Webhook patterns for receiving, verifying (HMAC), and idempotently processing third-party events. Covers Stripe, GitHub, and generic webhook patterns, delivery guarantees, retry handling, and testing.

wasm-patterns

8
from marvinrichter/clarc

WebAssembly patterns: wasm-pack, wasm-bindgen (JS↔Wasm interop), WASI, Component Model, wasm-opt, Rust-to-WASM compilation, JS integration (web workers, streaming instantiation), and production deployment (CDN, Content-Type headers).

ux-micro-patterns

8
from marvinrichter/clarc

UX micro-patterns for every product state: Empty States, Loading States (skeleton screens, spinners, optimistic UI), Error States, Success States, Confirmation Dialogs, Onboarding Flows, and Progressive Disclosure. These patterns apply to every feature — done wrong, they're the biggest source of user confusion.

typescript-patterns

8
from marvinrichter/clarc

TypeScript patterns — type system best practices, strict mode, utility types, generics, discriminated unions, error handling with Result types, and module organization. Core patterns for production TypeScript.

typescript-patterns-advanced

8
from marvinrichter/clarc

Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.

typescript-monorepo-patterns

8
from marvinrichter/clarc

TypeScript monorepo patterns with Turborepo + pnpm workspaces. Covers package structure, shared configs, task pipeline caching, build orchestration, and publishing strategy.

terraform-patterns

8
from marvinrichter/clarc

Infrastructure as Code with Terraform — project structure, remote state, modules, workspace strategy, AWS/GCP patterns, CI/CD integration, and security hardening. The standard for managing production infrastructure.

swiftui-patterns

8
from marvinrichter/clarc

SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices.

swift-patterns

8
from marvinrichter/clarc

Core Swift patterns — value vs reference types, protocols, generics, optionals, Result, error handling, Codable, and module organization. Foundation for all Swift development.

swift-patterns-advanced

8
from marvinrichter/clarc

Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.