agentleFS
foxj77foxj77/claude-code-skills/external-secrets-troubleshooting/SKILL.md

external-secrets-troubleshooting

Skill19 starsChanged 10 months agoMITView on GitHub

Use when ExternalSecret or SecretStore resources show errors, when secrets are not syncing from external providers like AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, or GCP Secret Manager, or when secret data is stale or missing

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When the appropriate MCP servers are connected, prefer these over raw kubectl where available:

    line 282

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

External Secrets Operator Troubleshooting

Diagnose failures in the External Secrets Operator (ESO) — the controller that synchronises secrets from external stores (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, GCP Secret Manager, etc.) into Kubernetes Secrets.

Keywords

external-secrets, externalsecret, secretstore, clustersecretstore, eso, vault, aws-secrets-manager, azure-key-vault, gcp-secret-manager, secret-sync, secret-rotation, provider, authentication, secret-data, pushsecret

When to Use This Skill

  • ExternalSecret resources show SecretSyncedError or Ready: False
  • SecretStore or ClusterSecretStore shows authentication errors
  • Kubernetes Secrets are not being created or contain stale data
  • Secret rotation is not triggering updates
  • PushSecret is failing to write back to external stores
  • Application pods fail because expected secrets are missing
  • Provider-specific connectivity or permission issues

When NOT to Use

  • Secrets exist but TLS certs are not being issued → use cert-manager-troubleshooting
  • Secret data is correct but the consuming pod fails → use k8s-namespace-troubleshooting
  • ESO resources are delivered by Flux and not appearing → use flux-troubleshooting

Related Skills

  • kyverno-troubleshooting - Policies may block Secret creation
  • cert-manager-troubleshooting - TLS certs stored as secrets
  • external-dns-troubleshooting - Shared provider auth patterns
  • k8s-namespace-troubleshooting - General namespace diagnosis
  • k8s-security-hardening - Secrets management and access control
  • flux-troubleshooting - GitOps delivery of ESO resources

Quick Reference

TaskCommand
Check operator podskubectl get pods -n external-secrets
List ExternalSecretskubectl get externalsecret -A
List SecretStoreskubectl get secretstore -A && kubectl get clustersecretstore
Check sync statuskubectl get externalsecret -A -o wide
View operator logskubectl logs -n external-secrets deploy/external-secrets --tail=200
Describe failing secretkubectl describe externalsecret NAME -n NS

Diagnostic Workflow

Secret not appearing in cluster?
├─ ExternalSecret exists?
│   ├─ No → ExternalSecret resource is missing
│   └─ Yes → Check ExternalSecret status
│       ├─ SecretSyncedError → Check SecretStore (Section 2)
│       ├─ InvalidStoreRef → Store name/namespace mismatch (Section 2)
│       └─ Ready: True but Secret wrong → Data mapping issue (Section 4)
├─ SecretStore/ClusterSecretStore healthy?
│   ├─ No → Provider auth issue (Section 3)
│   └─ Yes → Check ExternalSecret spec
│       ├─ Key not found at provider → Wrong path/key (Section 4)
│       ├─ Permission denied → IAM/RBAC at provider (Section 3)
│       └─ Sync interval too long → Adjust refreshInterval
└─ Secret exists but data is stale?
    ├─ refreshInterval too long → Reduce interval
    ├─ Provider value unchanged → Check provider-side value
    └─ Rotation not triggered → Check rotation config (Section 5)

Section 1: Operator Health

# Check ESO controller pods
kubectl get pods -n external-secrets -o wide
kubectl describe deploy/external-secrets -n external-secrets

# Check CRDs are installed
kubectl get crd | grep external-secrets

# Expected CRDs
# clustersecretstores.external-secrets.io
# externalsecrets.external-secrets.io
# secretstores.external-secrets.io
# pushsecrets.external-secrets.io

# Operator logs
kubectl logs -n external-secrets deploy/external-secrets --tail=200
kubectl logs -n external-secrets deploy/external-secrets-webhook --tail=100 2>/dev/null
kubectl logs -n external-secrets deploy/external-secrets-cert-controller --tail=100 2>/dev/null

# Check webhook (common failure point)
kubectl get validatingwebhookconfigurations | grep external-secrets
kubectl get mutatingwebhookconfigurations | grep external-secrets

Webhook Issues

SymptomCauseDiagnostic
ExternalSecret creation rejectedWebhook cert expired or webhook pod downCheck cert-controller pod status and webhook TLS cert
Timeout on create/updateWebhook pod unreachableCheck network policies and webhook Service endpoints
"connection refused" on admissionWebhook deployment scaled to 0Check webhook deployment replica count

Section 2: SecretStore and ClusterSecretStore

# Check SecretStore status
kubectl get secretstore -n ${NS} -o wide
kubectl describe secretstore ${STORE_NAME} -n ${NS}

# Check ClusterSecretStore status
kubectl get clustersecretstore -o wide
kubectl describe clustersecretstore ${STORE_NAME}

# Check the ExternalSecret's store reference
kubectl get externalsecret ${ES_NAME} -n ${NS} -o jsonpath='{.spec.secretStoreRef}'

Store Reference Problems

ErrorCauseDiagnostic
SecretStore "X" not foundStore doesn't exist in the namespaceCheck if SecretStore exists in the namespace or if ClusterSecretStore should be used
InvalidStoreRefName or kind mismatchVerify secretStoreRef.name and secretStoreRef.kind (SecretStore vs ClusterSecretStore)
Store Ready: FalseProvider auth or connectivity failureSee Section 3

Section 3: Provider Authentication

Full provider auth reference: See provider-authentication.md for generic diagnostic steps, auth method tables, and provider-specific issue matrices for AWS, Azure, GCP, Vault, and CloudFlare.

The commands and tables below are ESO-specific. For general auth debugging patterns, use the shared reference.

# Quick auth check — grep ESO logs for auth failures
kubectl logs -n external-secrets deploy/external-secrets --tail=200 | grep -iE 'credential|auth|forbidden|access denied|sts|azure|keyvault|vault|google|permission|seal'

Required Provider Permissions (ESO-Specific)

ProviderRequired Permissions
AWS Secrets Managersecretsmanager:GetSecretValue, secretsmanager:ListSecrets (for find), sts:AssumeRole (cross-account)
Azure Key VaultKey Vault Secrets User (or Key Vault Reader); Key Vault Certificates User for certs
HashiCorp VaultPolicy granting read on the secret path; K8s auth role allowing the SA and namespace
GCP Secret Managerroles/secretmanager.secretAccessor on the project or secret

Vault-Specific Checks (ESO)

ESO's Vault integration has unique failure modes beyond standard auth:

# Check Vault address and auth method in SecretStore
kubectl get secretstore -n ${NS} -o json | jq '.items[].spec.provider.vault | {server, auth: (.auth | keys)}'
Vault IssueSymptomDiagnostic
Vault sealed"Vault is sealed"Check Vault seal status and auto-unseal configuration
Token expired"permission denied"Check token validity and Kubernetes auth config
Wrong mount path"no handler for route"Check mountPath in SecretStore spec against Vault mount
Wrong KV versionEmpty data or wrong structureCheck version in provider spec against actual Vault KV version
K8s auth role wrong"invalid role"Verify role exists in Vault and allows the service account

Section 4: ExternalSecret Spec and Data Mapping

# Check ExternalSecret status and conditions
kubectl get externalsecret ${ES_NAME} -n ${NS} -o yaml

# Check the target Secret exists
kubectl get secret ${TARGET_SECRET} -n ${NS}

# Compare ExternalSecret keys with provider keys
kubectl get externalsecret ${ES_NAME} -n ${NS} -o jsonpath='{.spec.data[*].remoteRef.key}'

Data Mapping Problems

ErrorCauseDiagnostic
key "X" not foundRemote key/path doesn't exist at providerVerify exact path at provider console
property "X" not foundJSON property extraction failedCheck .remoteRef.property matches provider JSON structure
Secret exists but emptydecodingStrategy mismatchCheck decodingStrategy setting against provider encoding
Wrong encoding in SecretBase64 double-encodingCheck data vs dataFrom and encoding settings
Template rendering failedBad target.template syntaxCheck Go template syntax in ExternalSecret spec

ExternalSecret Spec Patterns

# Single key
spec:
  data:
    - secretKey: password          # Key in the K8s Secret
      remoteRef:
        key: /app/database         # Path at the provider
        property: password         # JSON property (if value is JSON)

# Multiple keys from one provider secret (dataFrom)
spec:
  dataFrom:
    - extract:
        key: /app/database         # All JSON keys become Secret keys

# Template for custom formatting
spec:
  target:
    template:
      data:
        connection: "host={{ .host }} port={{ .port }} user={{ .user }}"

Section 5: Refresh and Rotation

# Check refresh interval
kubectl get externalsecret ${ES_NAME} -n ${NS} -o jsonpath='{.spec.refreshInterval}'

# Check last sync time
kubectl get externalsecret ${ES_NAME} -n ${NS} -o jsonpath='{.status.refreshTime}'

# Check sync conditions
kubectl get externalsecret ${ES_NAME} -n ${NS} -o json | jq '.status.conditions'
ProblemCauseDiagnostic
Secret not updating after provider changerefreshInterval too longCheck refreshInterval value; recommend reducing for active development
Sync successful but value unchangedCached at provider (e.g., Vault lease)Check provider-side caching/leasing
refreshInterval: 0Sync disabled — only runs onceCheck if refreshInterval is intentionally set to 0

Section 6: PushSecret Issues

# Check PushSecret status
kubectl get pushsecret -n ${NS} -o wide
kubectl describe pushsecret ${PS_NAME} -n ${NS}

# Logs for PushSecret errors
kubectl logs -n external-secrets deploy/external-secrets --tail=200 | grep -iE 'push|write|put'
ProblemCauseDiagnostic
PushSecret SyncErrorWrite permissions missing at providerCheck provider IAM permissions for write access
"secret already exists"Conflict with existing provider secretCheck if updatePolicy is set and if the key already exists at the provider
PushSecret ignoredCRD not installed or feature not enabledCheck if pushsecrets.external-secrets.io CRD exists

Common Root Cause Patterns

SymptomLikely Root CauseEvidence to Confirm
All ExternalSecrets failingSecretStore auth brokenkubectl get secretstore -n NS shows Ready: False
One ExternalSecret failingWrong remote key pathdescribe externalsecret shows "key not found"
Secret created but emptyData mapping or encoding issueCheck decodingStrategy and remoteRef.property
Intermittent sync failuresProvider rate limitingOperator logs show 429 or throttle errors
Secrets stale after provider updateRefresh interval too longstatus.refreshTime is old
Webhook rejection on createWebhook cert expiredRestart cert-controller, check webhook config
All secrets in one namespace failingNamespace-scoped SecretStore issueOther namespaces using ClusterSecretStore work

MCP Tools Available

When the appropriate MCP servers are connected, prefer these over raw kubectl where available:

  • mcp__flux-operator-mcp__get_kubernetes_resources - Query ExternalSecrets, SecretStores, and Kubernetes Secrets
  • mcp__flux-operator-mcp__get_kubernetes_logs - Retrieve ESO controller and webhook logs
  • mcp__flux-operator-mcp__get_kubernetes_metrics - Check ESO resource consumption

Common Mistakes

MistakeWhy It FailsInstead
Using SecretStore when ClusterSecretStore is neededEach namespace needs its own SecretStore copyUse ClusterSecretStore for shared provider access across namespaces
Setting refreshInterval: 0 expecting real-time syncDisables refresh entirely — secret syncs onceUse a short interval (1m) for near-real-time
Confusing data and dataFromdata maps individual keys; dataFrom extracts all keys from a remote secretUse dataFrom.extract for JSON secrets with multiple keys
Not checking the webhook when ExternalSecrets fail to createWebhook validation rejects malformed specs silentlyCheck webhook pod health and admission webhook configs
Decoding secrets to verify valuesExposes sensitive data in terminal and agent contextCompare secret key names and lengths only — never decode
Missing property for JSON secretsGets the entire JSON blob as a single valueSet .remoteRef.property to extract specific JSON fields

Behavioural Guidelines

  1. Check SecretStore health first — Most ExternalSecret failures trace back to a broken store.
  2. Never decode or display secret values — Compare key names, data lengths, and sync timestamps instead.
  3. Verify the exact remote path — Provider paths are case-sensitive and format-specific (e.g., /secret/data/app for Vault KV v2 vs /secret/app for v1).
  4. Check provider console — Confirm the secret exists at the provider before debugging ESO.
  5. Watch the operator logs — ESO logs are detailed and include the exact provider error message.
  6. Namespace matters — SecretStore is namespace-scoped; ClusterSecretStore is cluster-scoped. Wrong reference kind silently fails.
  7. Restart as a last resort — Diagnose first, restart the operator only after confirming the config is correct.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.