agentsclimarketplace

Argocd operations

Skill Goodsmileduck/claude-registry/plugins/kubernetes-skills/skills/argocd-operations

Designs and debugs ArgoCD ApplicationSets, picks generators, templates per-tenant deploys, configures sync waves and hooks, and untangles syncPolicy.automated prune/selfHeal. Use when working with ArgoCD, ApplicationSet, sync wave, GitOps, or per-tenant Application deploys.From its SKILL.md

Install
npx -y skills add Goodsmileduck/claude-registry --skill argocd-operations

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 3 commands, including `kubectl get applicationset <name> -n argocd -o yaml | yq '.status'` and 2 more.

SKILL.md

10.7 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

ArgoCD

GitOps posture (the rules behind every recommendation here)

Every recommendation in this skill assumes GitOps-mode: Git is the source of truth, the cluster is a downstream replica. That implies three hard rules:

  1. Edit the chart/values, not the live object. kubectl edit deploy/foo on an Argo-managed resource is reverted in ~3 minutes by selfHeal. The fix is a commit to the source repo. Temporary hotfixes are allowed only when (a) explicitly requested, (b) labelled as a hotfix, (c) followed by a TODO to backport.
  2. One owner per resource. If ArgoCD manages a resource, Terraform must not also write it. If you're moving ownership from Terraform → Argo (or vice versa), close the loop: either remove the resource from the losing side's source or add ignore_changes / Argo Ignore annotations. See the state-operations.md reference in the terraform-workflows skill for the Terraform side.
  3. --prune is the moral equivalent of terraform destroy. Any argocd app sync --prune, app delete --cascade, or applicationsSync change that could prune Applications requires per-invocation confirmation. List what would be pruned (argocd app diff --refresh) and pause before executing.

When to invoke

Scenarios:

  • You need one Application per X (cluster, tenant, directory, PR) and don't want to author them by hand.
  • A multi-tenant deploy (per-clinic, per-customer, per-env) needs to scale without copy-pasting Application manifests.
  • An ApplicationSet generated unexpected Applications, deleted ones you wanted to keep, or kept ones you wanted gone.
  • Sync waves aren't ordering as expected; PreSync/PostSync hooks aren't firing.
  • prune: true + selfHeal: true produced surprising behavior.

Always-on defaults (set these at the top of every ApplicationSet)

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: <name>
  namespace: argocd
spec:
  goTemplate: true                              # use Go templating, not fasttemplate
  goTemplateOptions: ["missingkey=error"]       # fail loudly on missing keys instead of ""
  syncPolicy:
    applicationsSync: create-update             # disallow auto-delete of generated Applications
    preserveResourcesOnDeletion: true           # if Application is deleted, leave the workloads

Why these matter:

  • goTemplate: truefasttemplate is the legacy engine; Go templates are the default for new ApplicationSets and required for range, conditionals, and complex parameter shaping.
  • missingkey=error — without it, a typo like {{.naem}} silently renders empty and you get an Application named prod- deploying to namespace -prod. This is the single most common ApplicationSet bug.
  • applicationsSync: create-update — the controller will not delete Applications when a generator stops emitting them. Critical for production; without it a transient API error from the SCM Provider generator can prune all your apps.
  • preserveResourcesOnDeletion: true — if a user accidentally deletes the ApplicationSet, the generated Applications and their workloads survive.

Pick a generator by what changes

What you haveUse generator
A fixed, small list of targets (envs, clusters)List
Many clusters registered in ArgoCD, identified by labels on the cluster SecretClusters
One Application per directory in a Git repo (e.g. apps/*)Git directories
One Application per file matching a glob in a Git repo (e.g. tenants/*.yaml)Git files
Branches or PRs in a repo (preview environments)Pull Request
Combine two of the above (e.g. each app × each cluster)Matrix
Two sources whose results should be merged on a key (e.g. cluster name)Merge
Repos discovered from a GitHub/GitLab orgSCM Provider
Cluster set decided by an external controllerCluster Decision Resource

Cluster generator — the multi-tenant workhorse

For "one Application per cluster matching these labels":

generators:
- clusters:
    selector:
      matchLabels:
        argocd.argoproj.io/secret-type: cluster   # exclude the in-cluster default
        type: workload
        env: prod

The matching cluster Secrets must be labelled appropriately:

apiVersion: v1
kind: Secret
metadata:
  name: cluster-clinic-eu-1
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster      # required for ArgoCD to treat as a cluster
    type: workload
    env: prod
    region: eu
    tenant: clinic-eu-1                          # custom labels become template vars
type: Opaque
data:
  name: <base64>
  server: <base64>
  config: <base64-of-rest-config-json>

In templates, label values are available as {{.metadata.labels.tenant}} (when goTemplate: true). Common parameters injected by the cluster generator:

  • {{.name}} — the secret's name field
  • {{.server}} — the cluster API URL
  • {{.metadata.labels.X}} — any label on the cluster secret

Matrix generator — one app per (X × Y)

Most common multi-tenant pattern: per-tenant directory in Git, fanned out across N clusters.

generators:
- matrix:
    generators:
      - git:
          repoURL: https://github.com/org/tenants.git
          revision: HEAD
          directories:
            - path: tenants/*
      - clusters:
          selector:
            matchLabels:
              argocd.argoproj.io/secret-type: cluster
              env: prod
template:
  metadata:
    name: '{{.path.basename}}-{{.name}}'              # tenant-cluster
  spec:
    project: default
    source:
      repoURL: https://github.com/org/tenants.git
      targetRevision: HEAD
      path: '{{.path.path}}'                          # tenants/<tenant>
    destination:
      server: '{{.server}}'
      namespace: '{{.path.basename}}'
    syncPolicy:
      syncOptions: [CreateNamespace=true]
      automated:
        prune: true
        selfHeal: true

Result: cartesian product. 3 tenants × 5 clusters = 15 Applications. If you want a sparse mapping (tenant A only on cluster X), use the Merge generator on a key both sides emit.

sync-wave gotchas

Annotate resources to order sync within a single Application:

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "-1"     # negative waves run first

Rules that bite:

  • Sync waves order resources within one Application, not Applications relative to each other. To order Applications, use a separate ApplicationSet with sync waves on the Applications themselves, or use App-of-Apps with waves.
  • Default wave is 0. Resources with no annotation are wave 0 — they sync alongside other wave-0 resources, not after them.
  • Waves apply per sync phase. PreSync hooks all run before any Sync-phase resource regardless of wave; PostSync after all Sync-phase resources.
  • Finalizers can stall a wave indefinitely. If wave -1 includes a resource whose finalizer hangs, wave 0 never starts.

Hooks — PreSync, Sync, PostSync, SyncFail, PostDelete

metadata:
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded   # clean up after success
HookFires whenCommon use
PreSyncBefore sync startsDB migrations, ConfigMap warming
Sync (default)During sync, ordered by waveNormal resources
PostSyncAfter all Sync resources are HealthySmoke tests, cache warmers
SyncFailAfter a failed syncNotifications, rollback triggers
PostDeleteAfter Application deletionCleanup external resources

Delete policies for hooks (where the hook resource lives after running):

  • HookSucceeded — delete on success (typical for Jobs)
  • HookFailed — delete on failure (clean up failed Jobs)
  • BeforeHookCreation — delete previous instance before creating new (default for Jobs; required if your Job name is static)

automated sync policy — prune vs selfHeal

syncPolicy:
  automated:
    prune: true       # delete cluster resources removed from Git
    selfHeal: true    # revert manual changes in cluster to match Git

What each one actually does:

SettingWhen it triggersWhen it doesn't
prune: trueResource removed from Git → ArgoCD deletes it from clusterResource added in cluster but not in Git — leaves it alone (those are "extra resources," handled separately)
selfHeal: trueAnyone runs kubectl edit on a managed resource — ArgoCD reverts within ~3 minNew manual resource (no Git source) — left alone (same as prune's blind spot)

Trap: selfHeal reverts based on the rendered manifest. If your manifest uses a Helm chart whose default values changed between syncs, selfHeal can flap — it'll try to revert to the just-synced value, but on next sync the value re-renders differently.

Trap: prune: true + a generator that filters to zero results = mass deletion. Use applicationsSync: create-update at the ApplicationSet level as a safety net (it prevents the Application from being pruned, even if prune is true on the inner syncPolicy).

Debugging — when an ApplicationSet does the wrong thing

# Did the generator emit what you expected?
kubectl get applicationset <name> -n argocd -o yaml | yq '.status'

# What Applications exist with this owner reference?
kubectl get applications -n argocd -o json | \
  jq '.items[] | select(.metadata.ownerReferences[]?.name=="<appset-name>") | .metadata.name'

# Controller logs
kubectl logs -n argocd deploy/argocd-applicationset-controller --tail=200 | grep -i <appset-name>

Common diagnoses:

SymptomLikely cause
Generator runs but produces 0 ApplicationsSelector matches no cluster secrets; check labels — most commonly argocd.argoproj.io/secret-type: cluster is missing on the secret
Application names collideTemplate doesn't include enough discriminators (e.g. just {{.name}} when matrix produces overlapping names) — add tenant/path/cluster to the name
Applications stuck OutOfSyncgoTemplate: false (legacy fasttemplate) silently rendered an empty field — check for name: prod- or namespace: -
Pruned all Applications unexpectedlyThe generator's data source went away (e.g. Git repo unreachable, SCM token expired) and applicationsSync wasn't set to create-update
One Application syncs, others don'tPer-Application syncPolicy.automated not set in template; only sync-policy on the AppSet itself doesn't propagate to generated Apps

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most operations skills give in ~2.5k tokens

Counted across 483 of the 484 authors here whose files we hold, read 2026-08-07

  • Collect monitoring data throughout the simulationin 14 of 483, across 6 files
  • Set the random seed for reproducibilityin 14 of 483, across 6 files
  • Validate simulations against analytical solutionsin 12 of 483, across 4 files
  • Clarify goals, constraints, and inputsin 11 of 483, across 2 files
  • Implement contract tests for integration pointsin 11 of 483, across 2 files
  • Implement strangler fig infrastructure with API gatewayin 11 of 483, across 2 files
  • Audit modernized components for security vulnerabilitiesin 11 of 483, across 2 files
  • Avoid Python blocking calls in processesin 10 of 483, across 3 files
  • Use resource context managers for automatic cleanupin 9 of 483, across 2 files
  • Maintain consistent time unitsin 9 of 483, across 2 files
  • Validate outcomes against success criteriain 8 of 483, across 1 file
  • Analyze the legacy codebase for technical debtin 8 of 483, across 1 file

Said here and by no other author read

  • edit git sources not live cluster objects
  • maintain a single owner per managed resource
  • require confirmation before executing destructive prune commands
  • set goTemplate to true
  • set missingkey to error
  • set applicationsSync to create-update

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.