{"id":980,"date":"2026-05-06T01:38:50","date_gmt":"2026-05-06T01:38:50","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=980"},"modified":"2026-05-06T01:49:46","modified_gmt":"2026-05-06T01:49:46","slug":"master-guide-external-secrets-operator-in-kubernetes","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/master-guide-external-secrets-operator-in-kubernetes\/","title":{"rendered":"Master Guide: External Secrets Operator in Kubernetes"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-1024x683.png\" alt=\"\" class=\"wp-image-982\" srcset=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-1024x683.png 1024w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-300x200.png 300w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-768x512.png 768w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">1. Why secret management matters in Kubernetes<\/h2>\n\n\n\n<p>Most applications need sensitive values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DB username\nDB password\nAPI keys\nJWT signing keys\nTLS certificates\nOAuth client secrets\nRedis password\nKafka credentials\nThird-party SaaS tokens\n<\/code><\/pre>\n\n\n\n<p>In Kubernetes, the native object for this is a <code>Secret<\/code>.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: database-secret\n  namespace: payments\ntype: Opaque\ndata:\n  username: YWRtaW4=\n  password: cGFzc3dvcmQxMjM=\n<\/code><\/pre>\n\n\n\n<p>At first glance, this looks \u201csecure\u201d because the values are encoded. But this is only <strong>base64 encoding<\/strong>, not encryption. Kubernetes documentation explicitly says Secret values are base64 encoded and stored unencrypted by default unless encryption at rest is configured. It also recommends least-privilege access to Secrets through RBAC. (<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/security\/secrets-good-practices\/\">Kubernetes<\/a>)<\/p>\n\n\n\n<p>So the problem is not just \u201chow do we create Secrets?\u201d The real problems are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>How do we avoid storing secrets in Git?\nHow do we rotate secrets safely?\nHow do we centralize secret ownership?\nHow do we avoid manually copying secrets into every namespace?\nHow do we let apps consume secrets without giving every app cloud-secret-manager access?\nHow do we audit and govern secret access?\n<\/code><\/pre>\n\n\n\n<p>That is where <strong>External Secrets Operator<\/strong>, commonly called <strong>ESO<\/strong>, becomes useful.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. What is an Operator in Kubernetes?<\/h1>\n\n\n\n<p>A Kubernetes <strong>Operator<\/strong> is software that extends Kubernetes behavior using <strong>custom resources<\/strong> and <strong>controllers<\/strong>. Kubernetes describes Operators as extensions that use custom resources to manage applications and their components, following the Kubernetes control-loop principle. (<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/extend-kubernetes\/operator\/\">Kubernetes<\/a>)<\/p>\n\n\n\n<p>Simple explanation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You define the desired state.\nThe Operator watches that desired state.\nThe Operator compares desired state vs actual state.\nThe Operator takes action to make reality match the desired state.\n<\/code><\/pre>\n\n\n\n<p>This is the same idea Kubernetes already uses internally.<\/p>\n\n\n\n<p>For example, with a normal Deployment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spec:\n  replicas: 3\n<\/code><\/pre>\n\n\n\n<p>You are saying:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>I want 3 pods.\n<\/code><\/pre>\n\n\n\n<p>The Kubernetes Deployment controller keeps checking:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Do 3 pods exist?\nIf no, create more.\nIf too many, remove some.\nIf a pod dies, replace it.\n<\/code><\/pre>\n\n\n\n<p>An Operator does the same idea, but for custom application logic.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2.1 Operator pattern in simple words<\/h2>\n\n\n\n<p>Imagine a human DevOps engineer manually doing this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Check AWS Secrets Manager.\nRead the DB password.\nCreate a Kubernetes Secret.\nUpdate the Secret when the value changes.\nKeep doing this every hour.\n<\/code><\/pre>\n\n\n\n<p>Instead of a human doing that repeatedly, an Operator automates it.<\/p>\n\n\n\n<p>So with ESO:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You create ExternalSecret YAML.\nESO watches that YAML.\nESO reads AWS Secrets Manager \/ Vault \/ Azure Key Vault \/ etc.\nESO creates or updates a Kubernetes Secret.\nYour application consumes that Kubernetes Secret.\n<\/code><\/pre>\n\n\n\n<p>That is the Operator pattern.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. What is External Secrets Operator?<\/h1>\n\n\n\n<p><strong>External Secrets Operator<\/strong> is a Kubernetes Operator that integrates Kubernetes with external secret management systems such as AWS Secrets Manager, HashiCorp Vault, Google Secret Manager, Azure Key Vault, IBM Cloud Secrets Manager, CyberArk, Pulumi ESC, and others. The operator reads values from external APIs and injects them into Kubernetes Secrets. (<a href=\"https:\/\/external-secrets.io\/latest\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<p>The official goal of ESO is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Synchronize secrets from external APIs into Kubernetes.\n<\/code><\/pre>\n\n\n\n<p>ESO provides custom Kubernetes resources such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExternalSecret\nSecretStore\nClusterSecretStore\nClusterExternalSecret\nPushSecret\nClusterPushSecret\nGenerators\nClusterGenerator\n<\/code><\/pre>\n\n\n\n<p>The core resources allow you to describe:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Where the external secret manager is\nHow to authenticate to it\nWhich secret values to fetch\nWhat Kubernetes Secret should be created\nHow often it should refresh\nHow the final Kubernetes Secret should look\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. ESO in one diagram<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>+-------------------------+\n| External Secret Manager |\n| AWS Secrets Manager     |\n| Vault                   |\n| Azure Key Vault         |\n| Google Secret Manager   |\n+-----------+-------------+\n            |\n            | fetch secret values\n            v\n+-------------------------+\n| External Secrets        |\n| Operator Controller     |\n+-----------+-------------+\n            |\n            | creates \/ updates\n            v\n+-------------------------+\n| Kubernetes Secret       |\n| db-credentials          |\n+-----------+-------------+\n            |\n            | mounted\/env var\n            v\n+-------------------------+\n| Application Pod         |\n+-------------------------+\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-merpress-mermaidjs diagram-source-mermaid\"><pre class=\"mermaid\">flowchart LR\n\n    subgraph AWS[\"AWS Cloud\"]\n        A[\"AWS Secrets Manager\"]\n    end\n\n    subgraph K8S[\"Kubernetes Cluster\"]\n        B[\"ExternalSecret \/ SecretStore\"]\n        C[\"External Secrets Operator\"]\n        D[\"Kubernetes Secret\"]\n        E[\"Application Pod\"]\n    end\n\n    B -->|Configuration| C\n    A -->|Secret value| C\n    C -->|Creates \/ Updates| D\n    D -->|Consumed by| E<\/pre><\/div>\n\n\n\n<h1 class=\"wp-block-heading\">5. With ESO vs Without ESO<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">5.1 Without External Secrets Operator<\/h2>\n\n\n\n<p>In a basic Kubernetes setup, you create a Secret manually.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: app-db-secret\n  namespace: payments\ntype: Opaque\ndata:\n  username: YWRtaW4=\n  password: cGFzc3dvcmQxMjM=\n<\/code><\/pre>\n\n\n\n<p>Then your Deployment consumes it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: payment-api\n  namespace: payments\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: payment-api\n  template:\n    metadata:\n      labels:\n        app: payment-api\n    spec:\n      containers:\n        - name: payment-api\n          image: myrepo\/payment-api:1.0.0\n          env:\n            - name: DB_USERNAME\n              valueFrom:\n                secretKeyRef:\n                  name: app-db-secret\n                  key: username\n            - name: DB_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: app-db-secret\n                  key: password\n<\/code><\/pre>\n\n\n\n<p>This works, but it has problems:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Secret values may end up in Git.\nBase64 is not encryption.\nRotation is manual.\nSecret duplication across namespaces is painful.\nAuditability is weak.\nDifferent clusters may drift.\nCI\/CD pipelines may need direct access to secret values.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5.2 With External Secrets Operator<\/h2>\n\n\n\n<p>With ESO, the actual secret value lives in an external secret manager.<\/p>\n\n\n\n<p>Example in AWS Secrets Manager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"username\": \"admin\",\n  \"password\": \"SuperSecretPassword123\"\n}\n<\/code><\/pre>\n\n\n\n<p>In Kubernetes, you commit safe YAML like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: app-db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: username\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>ESO reads:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod\/payments\/db from AWS Secrets Manager\n<\/code><\/pre>\n\n\n\n<p>Then it creates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Kubernetes Secret: app-db-secret\n<\/code><\/pre>\n\n\n\n<p>Your application still consumes the normal Kubernetes Secret.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  - name: DB_PASSWORD\n    valueFrom:\n      secretKeyRef:\n        name: app-db-secret\n        key: password\n<\/code><\/pre>\n\n\n\n<p>The big difference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>The real password is not stored in Git.\nKubernetes only receives the synced Secret.\nRotation can be automated.\nThe external secret manager becomes the source of truth.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. Why use External Secrets Operator?<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">6.1 Main use cases<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Avoid storing secrets in Git<\/h3>\n\n\n\n<p>Instead of committing this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: Secret\ndata:\n  password: cGFzc3dvcmQxMjM=\n<\/code><\/pre>\n\n\n\n<p>You commit this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: ExternalSecret\nspec:\n  remoteRef:\n    key: prod\/payments\/db\n<\/code><\/pre>\n\n\n\n<p>That is much safer for GitOps.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">2. Centralized secret management<\/h3>\n\n\n\n<p>Secrets stay in systems designed for secrets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AWS Secrets Manager\nAWS Parameter Store\nHashiCorp Vault\nAzure Key Vault\nGoogle Secret Manager\n1Password\nCyberArk\nPulumi ESC\nIBM Cloud Secrets Manager\n<\/code><\/pre>\n\n\n\n<p>ESO supports many providers and the official docs list a large provider catalog. (<a href=\"https:\/\/external-secrets.io\/latest\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3. Automatic sync and rotation<\/h3>\n\n\n\n<p>If the value changes in the external secret manager, ESO can refresh the Kubernetes Secret based on <code>refreshInterval<\/code>.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spec:\n  refreshInterval: 1h\n<\/code><\/pre>\n\n\n\n<p>ESO\u2019s <code>ExternalSecret<\/code> supports refresh behavior such as <code>Periodic<\/code>, <code>CreatedOnce<\/code>, and <code>OnChange<\/code>. The default is <code>Periodic<\/code>. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/externalsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">4. Better separation of responsibility<\/h3>\n\n\n\n<p>Security\/platform team manages:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AWS Secrets Manager\nVault policies\nIAM roles\nKMS keys\nSecret rotation\n<\/code><\/pre>\n\n\n\n<p>Application teams manage:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExternalSecret manifests\nDeployment YAML\nApplication consumption\n<\/code><\/pre>\n\n\n\n<p>This gives cleaner ownership.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">5. Better GitOps workflow<\/h3>\n\n\n\n<p>With Argo CD or Flux, you can safely store these in Git:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SecretStore\nExternalSecret\nClusterExternalSecret\nDeployment\nService\n<\/code><\/pre>\n\n\n\n<p>But you do not store actual passwords.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">6. Multi-cluster consistency<\/h3>\n\n\n\n<p>Multiple clusters can pull the same secret source:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod EKS cluster\nDR cluster\nblue cluster\ngreen cluster\nregional clusters\n<\/code><\/pre>\n\n\n\n<p>This reduces manual copying.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">7. Namespace-level secret design<\/h3>\n\n\n\n<p>You can define:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>one SecretStore per namespace\nor one ClusterSecretStore for many namespaces\n<\/code><\/pre>\n\n\n\n<p>This helps with multi-team and multi-tenant Kubernetes design.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">8. Reduced CI\/CD secret exposure<\/h3>\n\n\n\n<p>Without ESO, CI\/CD often needs to inject secrets into Kubernetes.<\/p>\n\n\n\n<p>With ESO, CI\/CD only applies YAML. The secret value is fetched by the ESO controller inside the cluster.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. Important warning: ESO does not remove Kubernetes Secrets<\/h1>\n\n\n\n<p>This is a very important production point.<\/p>\n\n\n\n<p>ESO normally creates a regular Kubernetes <code>Secret<\/code>.<\/p>\n\n\n\n<p>That means you still need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RBAC restrictions\nEncryption at rest\nNamespace isolation\nLimited access to `get`, `list`, and `watch` secrets\nSecret lifecycle policies\nNetwork controls\nAudit logging\n<\/code><\/pre>\n\n\n\n<p>Kubernetes good-practice docs recommend encryption at rest for Secret data and least-privilege access to Secrets. (<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/security\/secrets-good-practices\/\">Kubernetes<\/a>)<\/p>\n\n\n\n<p>So the correct mental model is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ESO improves secret sourcing, syncing, rotation, and governance.\nESO does not magically make Kubernetes Secrets unreadable to anyone with Secret access.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. External Secrets Operator architecture<\/h1>\n\n\n\n<p>ESO has three major components:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Core Controller\nWebhook\nCert Controller\n<\/code><\/pre>\n\n\n\n<p>The official docs say External Secrets comes with a Core Controller, Webhook, and Cert Controller. The webhook supports conversion and validation for custom resources, while the cert-controller generates TLS credentials for the webhook and injects certificates into CRDs and webhook configuration. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/components\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.1 Core Controller<\/h2>\n\n\n\n<p>The core controller watches ESO custom resources such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExternalSecret\nSecretStore\nClusterSecretStore\nClusterExternalSecret\nPushSecret\nClusterPushSecret\n<\/code><\/pre>\n\n\n\n<p>It reconciles them into actual Kubernetes Secrets or external provider changes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8.2 Webhook<\/h2>\n\n\n\n<p>The webhook validates ESO custom resources before they are accepted by the Kubernetes API server.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You apply a wrong ExternalSecret.\nWebhook validates it.\nIf invalid, Kubernetes rejects the manifest.\n<\/code><\/pre>\n\n\n\n<p>This reduces bad configuration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8.3 Cert Controller<\/h2>\n\n\n\n<p>The webhook needs TLS certificates. The cert-controller manages those certificates for ESO\u2019s webhook.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-scaled.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"750\" src=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-1024x750.png\" alt=\"\" class=\"wp-image-985\" srcset=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-1024x750.png 1024w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-300x220.png 300w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-768x563.png 768w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-1536x1125.png 1536w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/05\/External-Secrets-Operator-flow-2048x1500.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">9. Core ESO resources<\/h1>\n\n\n\n<p>The current ESO API documentation includes packages such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets.io\/v1\nexternal-secrets.io\/v1alpha1\nexternal-secrets.io\/v1beta1\ngenerators.external-secrets.io\/v1alpha1\n<\/code><\/pre>\n\n\n\n<p>The current stable-looking examples in the latest docs commonly use <code>external-secrets.io\/v1<\/code> for core resources, while some advanced resources, especially generator-related ones, may still use <code>v1alpha1<\/code>. Always check your installed CRDs with <code>kubectl api-resources<\/code> and <code>kubectl explain<\/code>. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/spec\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<p>Main resources:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SecretStore\nClusterSecretStore\nExternalSecret\nClusterExternalSecret\nPushSecret\nClusterPushSecret\nGenerator resources\nClusterGenerator\n<\/code><\/pre>\n\n\n\n<p>Let\u2019s go one by one.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. SecretStore<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">10.1 What is SecretStore?<\/h2>\n\n\n\n<p>A <code>SecretStore<\/code> tells ESO how to connect to an external secret provider.<\/p>\n\n\n\n<p>It is <strong>namespace-scoped<\/strong>.<\/p>\n\n\n\n<p>That means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A SecretStore created in namespace payments is normally used by ExternalSecrets in namespace payments.\n<\/code><\/pre>\n\n\n\n<p>The official docs define <code>SecretStore<\/code> as a namespaced resource that specifies how to access an external API, and note that it cannot reference resources across namespaces by design. For cross-namespace use, ESO provides <code>ClusterSecretStore<\/code>. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/secretstore\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10.2 SecretStore example using AWS Secrets Manager with static AWS keys<\/h2>\n\n\n\n<p>This is simple, but not the best production pattern.<\/p>\n\n\n\n<p>First, create a Kubernetes Secret containing AWS credentials:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: aws-credentials\n  namespace: payments\ntype: Opaque\nstringData:\n  access-key: AKIAxxxxxxxxxxxx\n  secret-access-key: xxxxxxxxxxxxxxxxxxxxxxxxx\n<\/code><\/pre>\n\n\n\n<p>Then create the <code>SecretStore<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: SecretStore\nmetadata:\n  name: aws-secrets-store\n  namespace: payments\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      auth:\n        secretRef:\n          accessKeyIDSecretRef:\n            name: aws-credentials\n            key: access-key\n          secretAccessKeySecretRef:\n            name: aws-credentials\n            key: secret-access-key\n<\/code><\/pre>\n\n\n\n<p>Explanation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\n<\/code><\/pre>\n\n\n\n<p>Uses the ESO API group.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: SecretStore\n<\/code><\/pre>\n\n\n\n<p>Creates a namespace-scoped secret provider configuration.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>metadata.namespace: payments\n<\/code><\/pre>\n\n\n\n<p>This store belongs to the <code>payments<\/code> namespace.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>provider.aws.service: SecretsManager\n<\/code><\/pre>\n\n\n\n<p>Tells ESO to use AWS Secrets Manager.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>region: ap-south-1\n<\/code><\/pre>\n\n\n\n<p>AWS region where secrets exist.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auth.secretRef\n<\/code><\/pre>\n\n\n\n<p>Uses AWS access key and secret key stored in a Kubernetes Secret.<\/p>\n\n\n\n<p>This works, but for EKS production, IRSA \/ Pod Identity style authentication is usually better than static cloud credentials.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10.3 SecretStore example using AWS IAM Role<\/h2>\n\n\n\n<p>ESO\u2019s AWS provider docs recommend defining IAM roles with fine-grained access to individual secrets and passing the role through <code>spec.provider.aws.role<\/code>, so users of the store can access only the necessary secrets. (<a href=\"https:\/\/external-secrets.io\/latest\/provider\/aws-secrets-manager\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: SecretStore\nmetadata:\n  name: aws-secrets-store\n  namespace: payments\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      role: arn:aws:iam::123456789012:role\/payments-external-secrets-role\n      auth:\n        jwt:\n          serviceAccountRef:\n            name: payments-eso-sa\n<\/code><\/pre>\n\n\n\n<p>Explanation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>role\n<\/code><\/pre>\n\n\n\n<p>IAM role ESO should assume.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auth.jwt.serviceAccountRef\n<\/code><\/pre>\n\n\n\n<p>Uses a Kubernetes ServiceAccount token for AWS authentication, commonly used with IRSA on EKS.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. ClusterSecretStore<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">11.1 What is ClusterSecretStore?<\/h2>\n\n\n\n<p><code>ClusterSecretStore<\/code> is like <code>SecretStore<\/code>, but cluster-scoped.<\/p>\n\n\n\n<p>That means it can be referenced by <code>ExternalSecret<\/code> resources from multiple namespaces.<\/p>\n\n\n\n<p>The official docs describe <code>ClusterSecretStore<\/code> as a cluster-scoped <code>SecretStore<\/code> that can be referenced by all <code>ExternalSecrets<\/code> from all namespaces, useful as a central gateway to the secret backend. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/clustersecretstore\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11.2 When to use SecretStore vs ClusterSecretStore<\/h2>\n\n\n\n<p>Use <code>SecretStore<\/code> when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Each namespace should have its own secret access boundary.\nEach team owns its own namespace and secret access.\nYou want strict tenant separation.\n<\/code><\/pre>\n\n\n\n<p>Use <code>ClusterSecretStore<\/code> when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Platform team manages central secret provider access.\nMultiple namespaces need the same backend configuration.\nYou want less duplicated YAML.\nYou are comfortable controlling access through IAM\/RBAC\/provider policy.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11.3 ClusterSecretStore example for AWS Secrets Manager<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ClusterSecretStore\nmetadata:\n  name: aws-cluster-secrets-store\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      role: arn:aws:iam::123456789012:role\/eso-cluster-secret-reader\n      auth:\n        jwt:\n          serviceAccountRef:\n            name: external-secrets\n            namespace: external-secrets\n<\/code><\/pre>\n\n\n\n<p>Explanation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: ClusterSecretStore\n<\/code><\/pre>\n\n\n\n<p>This is cluster-wide.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>metadata has no namespace\n<\/code><\/pre>\n\n\n\n<p>Cluster-scoped resources do not belong to one namespace.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>serviceAccountRef.namespace\n<\/code><\/pre>\n\n\n\n<p>For a cluster-scoped store, specify where the ServiceAccount lives.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11.4 ExternalSecret using ClusterSecretStore<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-cluster-secrets-store\n    kind: ClusterSecretStore\n  target:\n    name: app-db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: username\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>Key point:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>secretStoreRef:\n  kind: ClusterSecretStore\n<\/code><\/pre>\n\n\n\n<p>This tells ESO to use the cluster-wide store.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. ExternalSecret<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">12.1 What is ExternalSecret?<\/h2>\n\n\n\n<p><code>ExternalSecret<\/code> is the resource you will use most often.<\/p>\n\n\n\n<p>It defines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Which external secret to fetch\nWhich keys\/properties to read\nWhich Kubernetes Secret to create\nHow often to refresh\nHow to template\/transform the output\n<\/code><\/pre>\n\n\n\n<p>The official docs say <code>ExternalSecret<\/code> describes what data should be fetched, how it should be transformed, and how it should be saved as a Kubernetes <code>Secret<\/code>. It supports <code>spec.data<\/code> for individual keys and <code>spec.dataFrom<\/code> for fetching multiple values. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/externalsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12.2 Basic ExternalSecret example<\/h2>\n\n\n\n<p>External secret in AWS Secrets Manager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"username\": \"admin\",\n  \"password\": \"SuperSecretPassword123\"\n}\n<\/code><\/pre>\n\n\n\n<p>ExternalSecret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: app-db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: username\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>This creates a Kubernetes Secret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: app-db-secret\n  namespace: payments\ntype: Opaque\ndata:\n  username: &lt;base64-value&gt;\n  password: &lt;base64-value&gt;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12.3 Important fields in ExternalSecret<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>refreshInterval<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>refreshInterval: 1h\n<\/code><\/pre>\n\n\n\n<p>How often ESO checks the external provider and updates the Kubernetes Secret.<\/p>\n\n\n\n<p>Common values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>15m\n30m\n1h\n24h\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>secretStoreRef<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>secretStoreRef:\n  name: aws-secrets-store\n  kind: SecretStore\n<\/code><\/pre>\n\n\n\n<p>Tells ESO which provider configuration to use.<\/p>\n\n\n\n<p>Could be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: SecretStore\n<\/code><\/pre>\n\n\n\n<p>or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: ClusterSecretStore\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>target.name<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>target:\n  name: app-db-secret\n<\/code><\/pre>\n\n\n\n<p>Name of the Kubernetes Secret that ESO should create\/update.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>target.creationPolicy<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>creationPolicy: Owner\n<\/code><\/pre>\n\n\n\n<p>Common options include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Owner\nMerge\nNone\n<\/code><\/pre>\n\n\n\n<p>Typical use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Owner: ESO owns the Secret lifecycle.\nMerge: ESO merges values into an existing Secret.\nNone: ESO does not create the Secret.\n<\/code><\/pre>\n\n\n\n<p>For most beginner use cases:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>creationPolicy: Owner\n<\/code><\/pre>\n\n\n\n<p>is the cleanest.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>data<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>data:\n  - secretKey: password\n    remoteRef:\n      key: prod\/payments\/db\n      property: password\n<\/code><\/pre>\n\n\n\n<p>This maps:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external secret property -&gt; Kubernetes Secret key\n<\/code><\/pre>\n\n\n\n<p>Meaning:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Read `password` from external secret `prod\/payments\/db`\nStore it as key `password` inside Kubernetes Secret\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>dataFrom<\/code><\/h3>\n\n\n\n<p>Use <code>dataFrom<\/code> when you want to import all keys from an external secret.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: app-db-secret\n    creationPolicy: Owner\n  dataFrom:\n    - extract:\n        key: prod\/payments\/db\n<\/code><\/pre>\n\n\n\n<p>If the external secret is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"username\": \"admin\",\n  \"password\": \"SuperSecretPassword123\",\n  \"host\": \"payments-db.xxxxxx.ap-south-1.rds.amazonaws.com\",\n  \"port\": \"5432\"\n}\n<\/code><\/pre>\n\n\n\n<p>The Kubernetes Secret will contain:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>username\npassword\nhost\nport\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. ExternalSecret refresh policies<\/h1>\n\n\n\n<p>ESO supports different refresh behaviors. The docs describe <code>CreatedOnce<\/code>, <code>Periodic<\/code>, and <code>OnChange<\/code>; if refresh policy is not specified, the default behavior is <code>Periodic<\/code>. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/externalsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">13.1 Periodic refresh<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: periodic-secret\n  namespace: payments\nspec:\n  refreshPolicy: Periodic\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: periodic-secret\n  data:\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>Use this when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Secrets may rotate.\nYou want Kubernetes Secret to stay updated.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">13.2 CreatedOnce<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: created-once-secret\n  namespace: payments\nspec:\n  refreshPolicy: CreatedOnce\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: created-once-secret\n  data:\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>Use this when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You want ESO to create the Secret once.\nYou do not want automatic updates after provider changes.\nYou prefer manual update control.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">13.3 OnChange<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: onchange-secret\n  namespace: payments\nspec:\n  refreshPolicy: OnChange\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: onchange-secret\n  data:\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>Use this when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You only want refresh when the ExternalSecret manifest changes.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. ExternalSecret templating<\/h1>\n\n\n\n<p>Templating is useful when your application expects a specific config file format.<\/p>\n\n\n\n<p>Example app expects:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DATABASE_URL=postgres:\/\/admin:password@host:5432\/payments\n<\/code><\/pre>\n\n\n\n<p>External secret in AWS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"username\": \"admin\",\n  \"password\": \"SuperSecretPassword123\",\n  \"host\": \"payments-db.xxxxxx.ap-south-1.rds.amazonaws.com\",\n  \"port\": \"5432\",\n  \"database\": \"payments\"\n}\n<\/code><\/pre>\n\n\n\n<p>ExternalSecret with template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-database-url\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-secrets-store\n    kind: SecretStore\n  target:\n    name: payment-database-url\n    creationPolicy: Owner\n    template:\n      type: Opaque\n      data:\n        DATABASE_URL: \"postgres:\/\/{{ .username }}:{{ .password }}@{{ .host }}:{{ .port }}\/{{ .database }}\"\n  data:\n    - secretKey: username\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: password\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n    - secretKey: host\n      remoteRef:\n        key: prod\/payments\/db\n        property: host\n    - secretKey: port\n      remoteRef:\n        key: prod\/payments\/db\n        property: port\n    - secretKey: database\n      remoteRef:\n        key: prod\/payments\/db\n        property: database\n<\/code><\/pre>\n\n\n\n<p>ESO will create a Kubernetes Secret containing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DATABASE_URL\n<\/code><\/pre>\n\n\n\n<p>This is useful when your app wants one connection string instead of many variables.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. Consuming ESO-created Secrets in Deployments<\/h1>\n\n\n\n<p>ESO creates normal Kubernetes Secrets, so applications consume them exactly the same way.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">15.1 Consume as environment variables<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: payment-api\n  namespace: payments\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: payment-api\n  template:\n    metadata:\n      labels:\n        app: payment-api\n    spec:\n      containers:\n        - name: payment-api\n          image: myrepo\/payment-api:1.0.0\n          env:\n            - name: DB_USERNAME\n              valueFrom:\n                secretKeyRef:\n                  name: app-db-secret\n                  key: username\n            - name: DB_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: app-db-secret\n                  key: password\n<\/code><\/pre>\n\n\n\n<p>Important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>If a Secret is consumed as environment variables, most apps need a pod restart to pick up changed values.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">15.2 Consume as mounted files<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: payment-api\n  namespace: payments\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: payment-api\n  template:\n    metadata:\n      labels:\n        app: payment-api\n    spec:\n      containers:\n        - name: payment-api\n          image: myrepo\/payment-api:1.0.0\n          volumeMounts:\n            - name: db-secret-volume\n              mountPath: \/etc\/secrets\/db\n              readOnly: true\n      volumes:\n        - name: db-secret-volume\n          secret:\n            secretName: app-db-secret\n<\/code><\/pre>\n\n\n\n<p>This creates files like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/etc\/secrets\/db\/username\n\/etc\/secrets\/db\/password\n<\/code><\/pre>\n\n\n\n<p>Mounted Secret volumes can update on disk, but your application must reload them or be restarted depending on how it reads configuration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. ClusterExternalSecret<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">16.1 What is ClusterExternalSecret?<\/h2>\n\n\n\n<p><code>ClusterExternalSecret<\/code> lets you create <code>ExternalSecret<\/code> resources across multiple namespaces.<\/p>\n\n\n\n<p>Example use case:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You have many namespaces.\nEach namespace needs the same Docker registry credentials.\nYou do not want to manually create ExternalSecret in every namespace.\n<\/code><\/pre>\n\n\n\n<p><code>ClusterExternalSecret<\/code> can match namespaces by labels and create ExternalSecrets in them.<\/p>\n\n\n\n<p>The official docs note that a <code>ClusterExternalSecret<\/code> creates one <code>ExternalSecret<\/code> per matched namespace, and each of those ExternalSecrets polls the upstream provider on its own refresh interval. For many namespaces, provider calls scale linearly, so the docs recommend a fan-out pattern for large namespace sets. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/clusterexternalsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">16.2 Label target namespaces<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Namespace\nmetadata:\n  name: payments\n  labels:\n    shared-secrets: enabled\n---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: orders\n  labels:\n    shared-secrets: enabled\n---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: shipping\n  labels:\n    shared-secrets: enabled\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">16.3 Create ClusterExternalSecret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ClusterExternalSecret\nmetadata:\n  name: shared-registry-secret\nspec:\n  externalSecretName: registry-secret\n  namespaceSelectors:\n    - matchLabels:\n        shared-secrets: enabled\n  refreshTime: 1h\n  externalSecretSpec:\n    refreshInterval: 1h\n    secretStoreRef:\n      name: aws-cluster-secrets-store\n      kind: ClusterSecretStore\n    target:\n      name: registry-secret\n      creationPolicy: Owner\n    dataFrom:\n      - extract:\n          key: prod\/shared\/docker-registry\n<\/code><\/pre>\n\n\n\n<p>This creates an <code>ExternalSecret<\/code> named:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>registry-secret\n<\/code><\/pre>\n\n\n\n<p>in each namespace matching:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shared-secrets: enabled\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">16.4 When to use ClusterExternalSecret<\/h2>\n\n\n\n<p>Use it for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Shared registry credentials\nShared monitoring credentials\nShared TLS trust bundles\nShared application license keys\nShared SaaS API keys\n<\/code><\/pre>\n\n\n\n<p>Avoid it when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Each service should have different credentials.\nEach namespace needs strict tenant isolation.\nProvider API rate limits are a concern across many namespaces.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. PushSecret<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">17.1 What is PushSecret?<\/h2>\n\n\n\n<p>Most ESO usage is pull-based:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>External provider -&gt; Kubernetes Secret\n<\/code><\/pre>\n\n\n\n<p><code>PushSecret<\/code> is the reverse idea:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Kubernetes Secret -&gt; external provider\n<\/code><\/pre>\n\n\n\n<p>This is useful when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A secret is generated inside Kubernetes.\nYou want to push it to AWS Secrets Manager \/ Vault \/ another provider.\nA controller creates credentials and you want them stored externally.\n<\/code><\/pre>\n\n\n\n<p>The current docs describe <code>PushSecret<\/code> with <code>dataTo<\/code> entries that specify which <code>SecretStore<\/code> to push to; each <code>dataTo<\/code> entry requires a <code>storeRef<\/code> to prevent accidental \u201cpush to all stores\u201d behavior. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/pushsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">17.2 Example source Kubernetes Secret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: generated-db-password\n  namespace: payments\ntype: Opaque\nstringData:\n  db-password: \"GeneratedPassword123\"\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">17.3 PushSecret example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1alpha1\nkind: PushSecret\nmetadata:\n  name: push-payment-db-password\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRefs:\n    - name: aws-secrets-store\n      kind: SecretStore\n  selector:\n    secret:\n      name: generated-db-password\n  data:\n    - match:\n        secretKey: db-password\n        remoteRef:\n          remoteKey: prod\/payments\/generated-db-password\n          property: password\n<\/code><\/pre>\n\n\n\n<p>Conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Read Kubernetes Secret `generated-db-password`\nTake key `db-password`\nPush it to external secret `prod\/payments\/generated-db-password`\nStore it as property `password`\n<\/code><\/pre>\n\n\n\n<p>Because PushSecret API details are still more advanced and may use alpha API versions depending on your ESO installation, always confirm with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl explain pushsecret.spec\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. ClusterPushSecret<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">18.1 What is ClusterPushSecret?<\/h2>\n\n\n\n<p><code>ClusterPushSecret<\/code> is the cluster-level version of <code>PushSecret<\/code>.<\/p>\n\n\n\n<p>Use case:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Multiple namespaces generate secrets.\nYou want cluster-level automation to push selected secrets to an external provider.\n<\/code><\/pre>\n\n\n\n<p>Example pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1alpha1\nkind: ClusterPushSecret\nmetadata:\n  name: push-generated-secrets\nspec:\n  namespaceSelectors:\n    - matchLabels:\n        push-secrets: enabled\n  pushSecretSpec:\n    refreshInterval: 1h\n    secretStoreRefs:\n      - name: aws-cluster-secrets-store\n        kind: ClusterSecretStore\n    selector:\n      secret:\n        name: generated-app-secret\n    data:\n      - match:\n          secretKey: token\n          remoteRef:\n            remoteKey: prod\/generated\/app-token\n            property: token\n<\/code><\/pre>\n\n\n\n<p>This is advanced and should be used carefully because it writes data outward to your secret manager.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. Generators<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">19.1 What are ESO Generators?<\/h2>\n\n\n\n<p>Generators allow ESO to generate or fetch dynamic values and expose them through ExternalSecrets.<\/p>\n\n\n\n<p>Examples include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Password generator\nUUID generator\nAWS ECR authorization token\nAWS STS session token\nVault dynamic secret\nGitHub access token\nWebhook generator\nSSH key generator\n<\/code><\/pre>\n\n\n\n<p>The current ESO docs list generator APIs under <code>generators.external-secrets.io\/v1alpha1<\/code>, and the ClusterGenerator docs describe <code>ClusterGenerator<\/code> as a cluster-wide wrapper so users do not have to redefine a generator in every namespace. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/spec\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">19.2 Password generator example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: generators.external-secrets.io\/v1alpha1\nkind: Password\nmetadata:\n  name: app-password-generator\n  namespace: payments\nspec:\n  length: 32\n  digits: 5\n  symbols: 5\n  symbolCharacters: \"-_$@\"\n  noUpper: false\n  allowRepeat: true\n<\/code><\/pre>\n\n\n\n<p>Then use it from an <code>ExternalSecret<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: generated-password-secret\n  namespace: payments\nspec:\n  refreshInterval: 24h\n  target:\n    name: generated-password-secret\n    creationPolicy: Owner\n  dataFrom:\n    - sourceRef:\n        generatorRef:\n          apiVersion: generators.external-secrets.io\/v1alpha1\n          kind: Password\n          name: app-password-generator\n<\/code><\/pre>\n\n\n\n<p>This creates a Kubernetes Secret with a generated password.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">19.3 ClusterGenerator example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: generators.external-secrets.io\/v1alpha1\nkind: ClusterGenerator\nmetadata:\n  name: cluster-password-generator\nspec:\n  kind: Password\n  generator:\n    passwordSpec:\n      length: 42\n      digits: 5\n      symbols: 5\n      symbolCharacters: \"-_$@\"\n      noUpper: false\n      allowRepeat: true\n<\/code><\/pre>\n\n\n\n<p>Use it from an ExternalSecret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: cluster-generated-secret\n  namespace: payments\nspec:\n  refreshInterval: 24h\n  target:\n    name: cluster-generated-secret\n    creationPolicy: Owner\n  dataFrom:\n    - sourceRef:\n        generatorRef:\n          apiVersion: generators.external-secrets.io\/v1alpha1\n          kind: ClusterGenerator\n          name: cluster-password-generator\n<\/code><\/pre>\n\n\n\n<p>This avoids redefining the generator in every namespace.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. Installing External Secrets Operator<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">20.1 Prerequisites<\/h2>\n\n\n\n<p>You need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A Kubernetes cluster\nkubectl configured\nHelm installed\nAccess to a secret backend, such as AWS Secrets Manager or Vault\nPermissions to install CRDs\n<\/code><\/pre>\n\n\n\n<p>ESO runs in the cluster as Kubernetes workloads and uses CRDs such as <code>ExternalSecret<\/code>, <code>SecretStore<\/code>, and <code>ClusterSecretStore<\/code>. The getting-started docs say ESO runs as a deployment and uses CRDs to configure secret-provider access and manage Kubernetes Secret resources. (<a href=\"https:\/\/external-secrets.io\/latest\/introduction\/getting-started\/?utm_source=chatgpt.com\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">20.2 Method 1: Install with Helm from chart repository<\/h2>\n\n\n\n<p>This is the most common method.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm repo add external-secrets https:\/\/charts.external-secrets.io\nhelm repo update\n\nhelm upgrade --install external-secrets external-secrets\/external-secrets \\\n  --namespace external-secrets \\\n  --create-namespace \\\n  --set installCRDs=true\n<\/code><\/pre>\n\n\n\n<p>The official getting-started docs show Helm installation from the chart repository and note that default install options install and manage CRDs as part of the Helm release; if you do not want Helm to manage CRDs, set <code>installCRDs=false<\/code>. (<a href=\"https:\/\/external-secrets.io\/latest\/introduction\/getting-started\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<p>Verify:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get pods -n external-secrets\n<\/code><\/pre>\n\n\n\n<p>Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets\nexternal-secrets-webhook\nexternal-secrets-cert-controller\n<\/code><\/pre>\n\n\n\n<p>Verify CRDs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get crds | grep external-secrets\n<\/code><\/pre>\n\n\n\n<p>Check API resources:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl api-resources | grep external\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">20.3 Method 2: Install CRDs separately, then Helm chart<\/h2>\n\n\n\n<p>This is common in production because some teams prefer managing CRDs separately from Helm releases.<\/p>\n\n\n\n<p>Step 1: Apply CRDs.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f https:\/\/raw.githubusercontent.com\/external-secrets\/external-secrets\/&lt;VERSION&gt;\/deploy\/crds\/bundle.yaml --server-side\n<\/code><\/pre>\n\n\n\n<p>Step 2: Install ESO without Helm managing CRDs.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm upgrade --install external-secrets external-secrets\/external-secrets \\\n  --namespace external-secrets \\\n  --create-namespace \\\n  --set installCRDs=false\n<\/code><\/pre>\n\n\n\n<p>Why use this method?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Safer CRD upgrades\nBetter GitOps control\nAvoid accidental CRD removal\nPlatform team manages CRDs centrally\nApplication teams consume the API\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">20.4 Method 3: GitOps with Argo CD or Flux<\/h2>\n\n\n\n<p>For GitOps, store a HelmRelease \/ Application manifest.<\/p>\n\n\n\n<p>Example Argo CD Application:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: argoproj.io\/v1alpha1\nkind: Application\nmetadata:\n  name: external-secrets\n  namespace: argocd\nspec:\n  project: platform\n  source:\n    repoURL: https:\/\/charts.external-secrets.io\n    chart: external-secrets\n    targetRevision: 0.0.0\n    helm:\n      values: |\n        installCRDs: true\n  destination:\n    server: https:\/\/kubernetes.default.svc\n    namespace: external-secrets\n  syncPolicy:\n    automated:\n      prune: true\n      selfHeal: true\n    syncOptions:\n      - CreateNamespace=true\n<\/code><\/pre>\n\n\n\n<p>Replace <code>targetRevision<\/code> with your approved chart version.<\/p>\n\n\n\n<p>Production tip:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Pin chart versions.\nDo not deploy uncontrolled latest versions in production.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">20.5 Method 4: OperatorHub \/ OLM<\/h2>\n\n\n\n<p>Some environments, especially OpenShift-style platforms, use Operator Lifecycle Manager.<\/p>\n\n\n\n<p>OperatorHub describes an External Secrets Operator package that configures the official external-secrets Helm chart and can be installed through OLM. (<a href=\"https:\/\/operatorhub.io\/operator\/external-secrets-operator?utm_source=chatgpt.com\">operatorhub.io<\/a>)<\/p>\n\n\n\n<p>Use this method when:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Your platform standard is OLM.\nYou are on OpenShift.\nYour organization manages operators through OperatorHub.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. Production install example for EKS with IRSA<\/h1>\n\n\n\n<p>This is a common production pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ESO runs in namespace external-secrets.\nESO ServiceAccount is mapped to an IAM Role.\nIAM Role can read only approved secrets.\nExternalSecret objects pull from AWS Secrets Manager.\nApplications consume Kubernetes Secrets.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">21.1 IAM policy example<\/h2>\n\n\n\n<p>Example policy allowing read access to selected secrets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": &#91;\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": &#91;\n        \"secretsmanager:GetSecretValue\",\n        \"secretsmanager:DescribeSecret\"\n      ],\n      \"Resource\": &#91;\n        \"arn:aws:secretsmanager:ap-south-1:123456789012:secret:prod\/payments\/*\"\n      ]\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p>Optional if using KMS-encrypted secrets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"Effect\": \"Allow\",\n  \"Action\": &#91;\n    \"kms:Decrypt\"\n  ],\n  \"Resource\": &#91;\n    \"arn:aws:kms:ap-south-1:123456789012:key\/YOUR-KMS-KEY-ID\"\n  ]\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">21.2 Install ESO with ServiceAccount annotation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>helm upgrade --install external-secrets external-secrets\/external-secrets \\\n  --namespace external-secrets \\\n  --create-namespace \\\n  --set installCRDs=true \\\n  --set serviceAccount.annotations.\"eks\\.amazonaws\\.com\/role-arn\"=\"arn:aws:iam::123456789012:role\/eso-prod-role\"\n<\/code><\/pre>\n\n\n\n<p>Verify ServiceAccount:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get sa -n external-secrets\nkubectl describe sa external-secrets -n external-secrets\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">21.3 ClusterSecretStore using IRSA<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ClusterSecretStore\nmetadata:\n  name: aws-prod-secrets\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      auth:\n        jwt:\n          serviceAccountRef:\n            name: external-secrets\n            namespace: external-secrets\n<\/code><\/pre>\n\n\n\n<p>Explanation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ESO uses its own ServiceAccount.\nThat ServiceAccount is mapped to an IAM Role.\nThe IAM Role controls what AWS Secrets Manager secrets ESO can read.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. Full end-to-end example: AWS Secrets Manager to Kubernetes Pod<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">22.1 Create namespace<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Namespace\nmetadata:\n  name: payments\n  labels:\n    name: payments\n<\/code><\/pre>\n\n\n\n<p>Apply:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f namespace.yaml\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">22.2 Store secret in AWS Secrets Manager<\/h2>\n\n\n\n<p>Example secret name:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod\/payments\/db\n<\/code><\/pre>\n\n\n\n<p>Secret value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"username\": \"payments_user\",\n  \"password\": \"StrongPassword123\",\n  \"host\": \"payments-db.xxxxxx.ap-south-1.rds.amazonaws.com\",\n  \"port\": \"5432\",\n  \"database\": \"payments\"\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">22.3 Create ClusterSecretStore<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ClusterSecretStore\nmetadata:\n  name: aws-prod-secrets\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      auth:\n        jwt:\n          serviceAccountRef:\n            name: external-secrets\n            namespace: external-secrets\n<\/code><\/pre>\n\n\n\n<p>Apply:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f cluster-secret-store.yaml\n<\/code><\/pre>\n\n\n\n<p>Check status:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get clustersecretstore\nkubectl describe clustersecretstore aws-prod-secrets\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">22.4 Create ExternalSecret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: payment-db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: DB_USERNAME\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: DB_PASSWORD\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n    - secretKey: DB_HOST\n      remoteRef:\n        key: prod\/payments\/db\n        property: host\n    - secretKey: DB_PORT\n      remoteRef:\n        key: prod\/payments\/db\n        property: port\n    - secretKey: DB_NAME\n      remoteRef:\n        key: prod\/payments\/db\n        property: database\n<\/code><\/pre>\n\n\n\n<p>Apply:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f external-secret.yaml\n<\/code><\/pre>\n\n\n\n<p>Check:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get externalsecret -n payments\nkubectl describe externalsecret payment-db-secret -n payments\nkubectl get secret payment-db-secret -n payments\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">22.5 Create Deployment consuming the Secret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: payment-api\n  namespace: payments\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: payment-api\n  template:\n    metadata:\n      labels:\n        app: payment-api\n    spec:\n      containers:\n        - name: payment-api\n          image: nginx:1.27\n          env:\n            - name: DB_USERNAME\n              valueFrom:\n                secretKeyRef:\n                  name: payment-db-secret\n                  key: DB_USERNAME\n            - name: DB_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: payment-db-secret\n                  key: DB_PASSWORD\n            - name: DB_HOST\n              valueFrom:\n                secretKeyRef:\n                  name: payment-db-secret\n                  key: DB_HOST\n            - name: DB_PORT\n              valueFrom:\n                secretKeyRef:\n                  name: payment-db-secret\n                  key: DB_PORT\n            - name: DB_NAME\n              valueFrom:\n                secretKeyRef:\n                  name: payment-db-secret\n                  key: DB_NAME\n<\/code><\/pre>\n\n\n\n<p>Apply:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f deployment.yaml\n<\/code><\/pre>\n\n\n\n<p>Verify pod:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get pods -n payments\nkubectl describe pod -n payments -l app=payment-api\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">23. Full example using Vault<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">23.1 SecretStore for Vault<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: SecretStore\nmetadata:\n  name: vault-secret-store\n  namespace: payments\nspec:\n  provider:\n    vault:\n      server: \"https:\/\/vault.example.com\"\n      path: \"secret\"\n      version: \"v2\"\n      auth:\n        tokenSecretRef:\n          name: vault-token\n          key: token\n<\/code><\/pre>\n\n\n\n<p>Kubernetes Secret containing Vault token:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: vault-token\n  namespace: payments\ntype: Opaque\nstringData:\n  token: \"s.xxxxxxxx\"\n<\/code><\/pre>\n\n\n\n<p>ExternalSecret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: payment-db-secret\n  namespace: payments\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: vault-secret-store\n    kind: SecretStore\n  target:\n    name: payment-db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: DB_PASSWORD\n      remoteRef:\n        key: payments\/db\n        property: password\n<\/code><\/pre>\n\n\n\n<p>Production note:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Static Vault token in Kubernetes is easy for demos, but not ideal for production.\nPrefer Kubernetes auth, JWT auth, or cloud identity integration where possible.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. ESO resource list summary<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Resource<\/th><th>Scope<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>SecretStore<\/code><\/td><td>Namespace<\/td><td>Defines how to connect to an external provider for one namespace<\/td><\/tr><tr><td><code>ClusterSecretStore<\/code><\/td><td>Cluster<\/td><td>Defines provider access reusable across namespaces<\/td><\/tr><tr><td><code>ExternalSecret<\/code><\/td><td>Namespace<\/td><td>Pulls external secret data into a Kubernetes Secret<\/td><\/tr><tr><td><code>ClusterExternalSecret<\/code><\/td><td>Cluster<\/td><td>Creates ExternalSecrets across matching namespaces<\/td><\/tr><tr><td><code>PushSecret<\/code><\/td><td>Namespace<\/td><td>Pushes Kubernetes Secret data to external provider<\/td><\/tr><tr><td><code>ClusterPushSecret<\/code><\/td><td>Cluster<\/td><td>Pushes selected secrets from multiple namespaces<\/td><\/tr><tr><td><code>Password<\/code>, <code>UUID<\/code>, etc.<\/td><td>Namespace<\/td><td>Generates dynamic secret values<\/td><\/tr><tr><td><code>ClusterGenerator<\/code><\/td><td>Cluster<\/td><td>Reusable cluster-wide generator wrapper<\/td><\/tr><tr><td><code>Secret<\/code><\/td><td>Namespace<\/td><td>Final Kubernetes Secret consumed by apps<\/td><\/tr><tr><td><code>ServiceAccount<\/code><\/td><td>Namespace<\/td><td>Identity used by ESO to authenticate to providers<\/td><\/tr><tr><td><code>Role\/ClusterRole<\/code><\/td><td>Namespace\/Cluster<\/td><td>RBAC for ESO and secret access<\/td><\/tr><tr><td><code>ValidatingWebhookConfiguration<\/code><\/td><td>Cluster<\/td><td>Validates ESO custom resources<\/td><\/tr><tr><td><code>CustomResourceDefinition<\/code><\/td><td>Cluster<\/td><td>Installs ESO API types<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. Common commands for day-to-day ESO operations<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">25.1 List stores<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secretstore -A\nkubectl get clustersecretstore\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.2 List ExternalSecrets<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get externalsecret -A\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.3 Describe ExternalSecret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl describe externalsecret payment-db-secret -n payments\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.4 Check created Kubernetes Secret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secret payment-db-secret -n payments\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.5 Decode Secret for testing only<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secret payment-db-secret -n payments \\\n  -o jsonpath='{.data.DB_PASSWORD}' | base64 -d\n<\/code><\/pre>\n\n\n\n<p>Use this only for testing\/debugging. Do not casually decode production secrets.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">25.6 Check ESO controller logs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl logs -n external-secrets deploy\/external-secrets\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.7 Check webhook logs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl logs -n external-secrets deploy\/external-secrets-webhook\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.8 Check cert-controller logs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl logs -n external-secrets deploy\/external-secrets-cert-controller\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.9 Explain fields<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl explain externalsecret.spec\nkubectl explain externalsecret.spec.data\nkubectl explain secretstore.spec\nkubectl explain clustersecretstore.spec\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. ESO and namespace design<\/h1>\n\n\n\n<p>This connects directly to your earlier namespace discussion.<\/p>\n\n\n\n<p>ESO works best when namespace boundaries are clean.<\/p>\n\n\n\n<p>Example production layout:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets       -&gt; ESO controller\npayments-prod          -&gt; payment service secrets\norders-prod            -&gt; order service secrets\nshipping-prod          -&gt; shipping service secrets\nfrontend-prod          -&gt; frontend app secrets\nmonitoring-prod        -&gt; monitoring credentials\n<\/code><\/pre>\n\n\n\n<p>Then each namespace can have:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>its own ExternalSecret\nits own ServiceAccount\nits own NetworkPolicy\nits own ResourceQuota\nits own LimitRange\nits own RBAC boundary\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">26.1 One namespace for all services<\/h2>\n\n\n\n<p>Possible, but less clean.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod\n  payment-api\n  order-api\n  shipping-api\n  auth-api\n  notification-api\n  reporting-api\n  frontend\n<\/code><\/pre>\n\n\n\n<p>Problems:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>All ExternalSecrets live together.\nAll Kubernetes Secrets live together.\nRBAC becomes broader.\nSecret naming must be very careful.\nResourceQuota applies to the combined namespace.\nOne mistake can impact all services.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">26.2 One namespace per service<\/h2>\n\n\n\n<p>Cleaner for production:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>payments-prod\norders-prod\nshipping-prod\nauth-prod\nnotification-prod\nreporting-prod\nfrontend-prod\n<\/code><\/pre>\n\n\n\n<p>Benefits:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Per-service ExternalSecret\nPer-service RBAC\nPer-service quota\nPer-service NetworkPolicy\nCleaner ownership\nSmaller blast radius\nBetter auditability\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: db-secret\n  namespace: payments-prod\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: db-secret\n  dataFrom:\n    - extract:\n        key: prod\/payments\/db\n<\/code><\/pre>\n\n\n\n<p>And for orders:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: db-secret\n  namespace: orders-prod\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: db-secret\n  dataFrom:\n    - extract:\n        key: prod\/orders\/db\n<\/code><\/pre>\n\n\n\n<p>Same Secret name, different namespaces.<\/p>\n\n\n\n<p>That is clean.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. ESO RBAC best practices<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">27.1 Limit human access to Kubernetes Secrets<\/h2>\n\n\n\n<p>Do not give broad access like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resources:\n  - secrets\nverbs:\n  - get\n  - list\n  - watch\n<\/code><\/pre>\n\n\n\n<p>to everyone.<\/p>\n\n\n\n<p>This is risky because anyone with <code>get secret<\/code> can decode values.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">27.2 Application teams usually need ExternalSecret access, not Secret access<\/h2>\n\n\n\n<p>Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Developers can create\/update ExternalSecret.\nOnly platform\/security can read actual Kubernetes Secret values.\nApplications can mount\/use Secrets.\n<\/code><\/pre>\n\n\n\n<p>Example Role:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: rbac.authorization.k8s.io\/v1\nkind: Role\nmetadata:\n  name: external-secret-manager\n  namespace: payments-prod\nrules:\n  - apiGroups:\n      - external-secrets.io\n    resources:\n      - externalsecrets\n    verbs:\n      - get\n      - list\n      - watch\n      - create\n      - update\n      - patch\n      - delete\n<\/code><\/pre>\n\n\n\n<p>Avoid giving this unless required:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiGroups:\n  - \"\"\nresources:\n  - secrets\nverbs:\n  - get\n  - list\n  - watch\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. SecretStore vs ClusterSecretStore security design<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Pattern A: Central ClusterSecretStore<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>One ClusterSecretStore for prod.\nAll namespaces use it.\nIAM policy restricts access by secret path.\n<\/code><\/pre>\n\n\n\n<p>Pros:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Less duplicated YAML\nSimple platform management\nEasy onboarding\n<\/code><\/pre>\n\n\n\n<p>Cons:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Must be careful with IAM permissions\nA bad ExternalSecret may try to access wrong path\nRequires strong provider-side restrictions\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Pattern B: One SecretStore per namespace<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>payments-prod has payments SecretStore.\norders-prod has orders SecretStore.\nEach uses a separate IAM Role or provider auth.\n<\/code><\/pre>\n\n\n\n<p>Pros:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Strong tenant isolation\nLeast privilege is clearer\nGood for sensitive services\n<\/code><\/pre>\n\n\n\n<p>Cons:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>More YAML\nMore IAM roles\nMore operational overhead\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Pattern C: Hybrid<\/h2>\n\n\n\n<p>Recommended for many production systems:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ClusterSecretStore for shared low-risk secrets\nNamespace SecretStore for sensitive service-specific secrets\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ClusterSecretStore:\n  shared docker registry credentials\n  shared monitoring token\n\nSecretStore:\n  payments database password\n  auth signing key\n  customer PII encryption key\n<\/code><\/pre>\n\n\n\n<p>This is often the best balance.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">29. ESO and NetworkPolicy<\/h1>\n\n\n\n<p>ESO needs network access to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Kubernetes API server\nExternal secret provider endpoint\nDNS\nCloud metadata \/ STS endpoint depending on auth model\n<\/code><\/pre>\n\n\n\n<p>Example NetworkPolicy allowing ESO egress broadly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: networking.k8s.io\/v1\nkind: NetworkPolicy\nmetadata:\n  name: external-secrets-egress\n  namespace: external-secrets\nspec:\n  podSelector:\n    matchLabels:\n      app.kubernetes.io\/name: external-secrets\n  policyTypes:\n    - Egress\n  egress:\n    - {}\n<\/code><\/pre>\n\n\n\n<p>That is permissive.<\/p>\n\n\n\n<p>A stricter policy would allow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DNS\nAWS Secrets Manager endpoint\nAWS STS endpoint\nKubernetes API server\n<\/code><\/pre>\n\n\n\n<p>Exact egress rules depend on your CNI, cloud provider, and whether you use private endpoints.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">30. ESO and ResourceQuota \/ LimitRange<\/h1>\n\n\n\n<p>ESO itself should run in its own namespace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets\n<\/code><\/pre>\n\n\n\n<p>Add requests\/limits using Helm values.<\/p>\n\n\n\n<p>Example conceptual values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resources:\n  requests:\n    cpu: 100m\n    memory: 128Mi\n  limits:\n    cpu: 500m\n    memory: 512Mi\n\nwebhook:\n  resources:\n    requests:\n      cpu: 50m\n      memory: 64Mi\n    limits:\n      cpu: 200m\n      memory: 256Mi\n\ncertController:\n  resources:\n    requests:\n      cpu: 50m\n      memory: 64Mi\n    limits:\n      cpu: 200m\n      memory: 256Mi\n<\/code><\/pre>\n\n\n\n<p>Then install:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm upgrade --install external-secrets external-secrets\/external-secrets \\\n  --namespace external-secrets \\\n  --create-namespace \\\n  --values values.yaml\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">31. ESO production best practices<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">31.1 Run ESO in a dedicated namespace<\/h2>\n\n\n\n<p>Recommended:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets\n<\/code><\/pre>\n\n\n\n<p>Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>default\nkube-system\napplication namespaces\n<\/code><\/pre>\n\n\n\n<p>Reason:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cleaner RBAC\nCleaner monitoring\nBetter blast-radius control\nSeparate lifecycle\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.2 Use cloud-native identity instead of static credentials<\/h2>\n\n\n\n<p>For EKS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Use IRSA or EKS Pod Identity.\nAvoid long-lived AWS access keys.\n<\/code><\/pre>\n\n\n\n<p>For AKS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Use Azure Workload Identity.\n<\/code><\/pre>\n\n\n\n<p>For GKE:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Use Workload Identity.\n<\/code><\/pre>\n\n\n\n<p>For Vault:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Use Kubernetes auth or JWT auth instead of static root tokens.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.3 Use least privilege in external secret manager<\/h2>\n\n\n\n<p>Bad IAM policy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"Effect\": \"Allow\",\n  \"Action\": \"secretsmanager:*\",\n  \"Resource\": \"*\"\n}\n<\/code><\/pre>\n\n\n\n<p>Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"Effect\": \"Allow\",\n  \"Action\": &#91;\n    \"secretsmanager:GetSecretValue\",\n    \"secretsmanager:DescribeSecret\"\n  ],\n  \"Resource\": &#91;\n    \"arn:aws:secretsmanager:ap-south-1:123456789012:secret:prod\/payments\/*\"\n  ]\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.4 Use naming conventions<\/h2>\n\n\n\n<p>Good external secret paths:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod\/payments\/db\nprod\/payments\/redis\nprod\/payments\/stripe\nprod\/orders\/db\nprod\/orders\/kafka\nprod\/shared\/docker-registry\n<\/code><\/pre>\n\n\n\n<p>Avoid vague names:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>db\npassword\nsecret1\napp-secret\ntest\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.5 Avoid one giant shared secret<\/h2>\n\n\n\n<p>Not ideal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"payments_db_password\": \"...\",\n  \"orders_db_password\": \"...\",\n  \"auth_jwt_key\": \"...\",\n  \"stripe_key\": \"...\",\n  \"redis_password\": \"...\"\n}\n<\/code><\/pre>\n\n\n\n<p>Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>prod\/payments\/db\nprod\/orders\/db\nprod\/auth\/jwt\nprod\/payments\/stripe\nprod\/shared\/redis\n<\/code><\/pre>\n\n\n\n<p>This makes access control easier.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.6 Use separate ExternalSecrets per logical secret<\/h2>\n\n\n\n<p>Good:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>payment-db-secret\npayment-stripe-secret\npayment-redis-secret\n<\/code><\/pre>\n\n\n\n<p>Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>all-payment-secrets\n<\/code><\/pre>\n\n\n\n<p>Smaller Secrets are easier to rotate and audit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.7 Protect Kubernetes Secrets with RBAC<\/h2>\n\n\n\n<p>Do not give wide access to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secrets -A\n<\/code><\/pre>\n\n\n\n<p>That is essentially secret-admin access.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.8 Enable encryption at rest<\/h2>\n\n\n\n<p>ESO syncs values into Kubernetes Secrets, so Kubernetes Secret protection still matters.<\/p>\n\n\n\n<p>Enable encryption at rest for Secret data in etcd. Kubernetes recommends this for Secret objects. (<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/security\/secrets-good-practices\/\">Kubernetes<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.9 Monitor ESO<\/h2>\n\n\n\n<p>Monitor:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExternalSecret sync status\nController errors\nProvider API rate limits\nWebhook failures\nSecretStore readiness\nClusterSecretStore readiness\n<\/code><\/pre>\n\n\n\n<p>Useful commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get externalsecret -A\nkubectl describe externalsecret &lt;name&gt; -n &lt;namespace&gt;\nkubectl logs -n external-secrets deploy\/external-secrets\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31.10 Pin chart and image versions<\/h2>\n\n\n\n<p>Avoid uncontrolled upgrades.<\/p>\n\n\n\n<p>Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>targetRevision: 0.x.x\n<\/code><\/pre>\n\n\n\n<p>or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm upgrade --install ... --version 0.x.x\n<\/code><\/pre>\n\n\n\n<p>Test upgrades in non-prod first.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">32. Common troubleshooting scenarios<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">32.1 ExternalSecret not creating Kubernetes Secret<\/h2>\n\n\n\n<p>Check:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get externalsecret -n payments\nkubectl describe externalsecret payment-db-secret -n payments\n<\/code><\/pre>\n\n\n\n<p>Look for events like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SecretStore not found\nAccess denied\nRemote secret not found\nInvalid property\nProvider authentication failed\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32.2 SecretStore not ready<\/h2>\n\n\n\n<p>Check:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl describe secretstore aws-secrets-store -n payments\n<\/code><\/pre>\n\n\n\n<p>or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl describe clustersecretstore aws-prod-secrets\n<\/code><\/pre>\n\n\n\n<p>Common reasons:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Wrong AWS region\nWrong IAM role\nMissing service account annotation\nMissing KMS decrypt permission\nInvalid credentials\nProvider endpoint unreachable\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32.3 AccessDenied from AWS<\/h2>\n\n\n\n<p>Check IAM permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>secretsmanager:GetSecretValue\nsecretsmanager:DescribeSecret\nkms:Decrypt if using customer-managed KMS key\nsts:AssumeRole if using role assumption\n<\/code><\/pre>\n\n\n\n<p>Also check the secret ARN pattern.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32.4 Secret exists but app still has old password<\/h2>\n\n\n\n<p>Possible reasons:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>App consumed Secret as environment variable.\nPod has not restarted.\nApp does not reload mounted files.\nExternalSecret refresh interval has not passed.\nExternal provider value changed but same Kubernetes Secret value remained.\n<\/code><\/pre>\n\n\n\n<p>Restart Deployment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl rollout restart deployment payment-api -n payments\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32.5 Wrong property name<\/h2>\n\n\n\n<p>External AWS secret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"password\": \"abc\"\n}\n<\/code><\/pre>\n\n\n\n<p>ExternalSecret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>property: db_password\n<\/code><\/pre>\n\n\n\n<p>This will fail because <code>db_password<\/code> does not exist.<\/p>\n\n\n\n<p>Correct:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>property: password\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32.6 ClusterExternalSecret creates too many provider calls<\/h2>\n\n\n\n<p>The docs warn that ClusterExternalSecret creates an ExternalSecret per matched namespace, and each ExternalSecret polls independently. For large namespace sets, provider API calls can grow linearly, and the docs recommend a fan-out pattern using one source ExternalSecret plus the Kubernetes provider. (<a href=\"https:\/\/external-secrets.io\/latest\/api\/clusterexternalsecret\/\">external-secrets.io<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">33. Practical production folder structure<\/h1>\n\n\n\n<p>For GitOps:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>platform\/\n  external-secrets\/\n    namespace.yaml\n    helm-release.yaml\n    cluster-secret-store.yaml\n\napps\/\n  payments\/\n    namespace.yaml\n    external-secret-db.yaml\n    external-secret-redis.yaml\n    deployment.yaml\n    service.yaml\n  orders\/\n    namespace.yaml\n    external-secret-db.yaml\n    deployment.yaml\n    service.yaml\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apps\/payments\/external-secret-db.yaml\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: db-secret\n  namespace: payments-prod\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: db-secret\n    creationPolicy: Owner\n  dataFrom:\n    - extract:\n        key: prod\/payments\/db\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">34. Recommended design for your 7 production services<\/h1>\n\n\n\n<p>Since you were discussing whether all 7 services should run in one namespace, ESO gives another reason to separate services.<\/p>\n\n\n\n<p>A good production design:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>external-secrets\nservice-a-prod\nservice-b-prod\nservice-c-prod\nservice-d-prod\nservice-e-prod\nservice-f-prod\nservice-g-prod\n<\/code><\/pre>\n\n\n\n<p>Each service namespace gets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExternalSecret objects\nKubernetes Secrets\nServiceAccount\nDeployment\nNetworkPolicy\nResourceQuota\nLimitRange\nRBAC\n<\/code><\/pre>\n\n\n\n<p>Benefits:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>One service cannot accidentally consume another service\u2019s Secret.\nSecret names can be simple, like db-secret, redis-secret.\nRBAC can be scoped per service namespace.\nExternalSecret ownership is clearer.\nResourceQuota and LimitRange can protect each service separately.\nNetworkPolicy can be simpler.\nBlast radius is smaller.\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>payments-prod\/db-secret\norders-prod\/db-secret\nshipping-prod\/db-secret\n<\/code><\/pre>\n\n\n\n<p>Same Secret name, different namespace, clean separation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">35. End-to-end recommended production pattern<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Install ESO in external-secrets namespace.\n2. Use IRSA \/ workload identity for ESO.\n3. Create IAM roles with least privilege.\n4. Create ClusterSecretStore for shared low-risk secrets.\n5. Create per-namespace SecretStore for sensitive service-specific secrets.\n6. Create ExternalSecret per logical secret.\n7. Apps consume generated Kubernetes Secrets.\n8. Protect Kubernetes Secrets with RBAC.\n9. Enable encryption at rest.\n10. Monitor ESO status and logs.\n11. Use namespace separation for prod services.\n12. Keep real secret values out of Git.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">36. Complete sample manifest set<\/h1>\n\n\n\n<p>Below is a mini complete setup.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">36.1 Namespace<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Namespace\nmetadata:\n  name: payments-prod\n  labels:\n    app: payments\n    environment: prod\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36.2 ClusterSecretStore<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ClusterSecretStore\nmetadata:\n  name: aws-prod-secrets\nspec:\n  provider:\n    aws:\n      service: SecretsManager\n      region: ap-south-1\n      auth:\n        jwt:\n          serviceAccountRef:\n            name: external-secrets\n            namespace: external-secrets\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36.3 ExternalSecret for DB<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: db-secret\n  namespace: payments-prod\nspec:\n  refreshPolicy: Periodic\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: db-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: DB_USERNAME\n      remoteRef:\n        key: prod\/payments\/db\n        property: username\n    - secretKey: DB_PASSWORD\n      remoteRef:\n        key: prod\/payments\/db\n        property: password\n    - secretKey: DB_HOST\n      remoteRef:\n        key: prod\/payments\/db\n        property: host\n    - secretKey: DB_PORT\n      remoteRef:\n        key: prod\/payments\/db\n        property: port\n    - secretKey: DB_NAME\n      remoteRef:\n        key: prod\/payments\/db\n        property: database\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36.4 ExternalSecret for Redis<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: external-secrets.io\/v1\nkind: ExternalSecret\nmetadata:\n  name: redis-secret\n  namespace: payments-prod\nspec:\n  refreshInterval: 1h\n  secretStoreRef:\n    name: aws-prod-secrets\n    kind: ClusterSecretStore\n  target:\n    name: redis-secret\n    creationPolicy: Owner\n  data:\n    - secretKey: REDIS_PASSWORD\n      remoteRef:\n        key: prod\/payments\/redis\n        property: password\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36.5 Deployment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: payments-api\n  namespace: payments-prod\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: payments-api\n  template:\n    metadata:\n      labels:\n        app: payments-api\n    spec:\n      containers:\n        - name: payments-api\n          image: myrepo\/payments-api:1.0.0\n          env:\n            - name: DB_USERNAME\n              valueFrom:\n                secretKeyRef:\n                  name: db-secret\n                  key: DB_USERNAME\n            - name: DB_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: db-secret\n                  key: DB_PASSWORD\n            - name: DB_HOST\n              valueFrom:\n                secretKeyRef:\n                  name: db-secret\n                  key: DB_HOST\n            - name: DB_PORT\n              valueFrom:\n                secretKeyRef:\n                  name: db-secret\n                  key: DB_PORT\n            - name: DB_NAME\n              valueFrom:\n                secretKeyRef:\n                  name: db-secret\n                  key: DB_NAME\n            - name: REDIS_PASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: redis-secret\n                  key: REDIS_PASSWORD\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36.6 Verification commands<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get clustersecretstore\nkubectl describe clustersecretstore aws-prod-secrets\n\nkubectl get externalsecret -n payments-prod\nkubectl describe externalsecret db-secret -n payments-prod\n\nkubectl get secret -n payments-prod\nkubectl get secret db-secret -n payments-prod\n\nkubectl get pods -n payments-prod\nkubectl describe deployment payments-api -n payments-prod\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">37. Final mental model<\/h1>\n\n\n\n<p>Think of ESO like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SecretStore \/ ClusterSecretStore = Where and how to connect\nExternalSecret = What to fetch and what Kubernetes Secret to create\nKubernetes Secret = What the application consumes\nESO Controller = The automation that keeps them in sync\n<\/code><\/pre>\n\n\n\n<p>Without ESO:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Human \/ CI\/CD creates Kubernetes Secrets manually.\nSecrets may leak into Git or pipelines.\nRotation is painful.\n<\/code><\/pre>\n\n\n\n<p>With ESO:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>External secret manager is source of truth.\nKubernetes receives synced Secrets.\nGit stores only references, not secret values.\nRotation and governance become much cleaner.\n<\/code><\/pre>\n\n\n\n<p>For production, the best setup is usually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Dedicated ESO namespace\nWorkload identity \/ IRSA\nLeast-privilege IAM\/provider policies\nPer-service namespaces\nPer-service ExternalSecrets\nRBAC restrictions on Kubernetes Secrets\nEncryption at rest\nMonitoring and alerting for sync failures\n<\/code><\/pre>\n\n\n\n<p>That gives you a secure, scalable, and clean secret-management pattern for Kubernetes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Why secret management matters in Kubernetes Most applications need sensitive values: In Kubernetes, the native object for this is a Secret. Example: At first glance, this&#8230; <\/p>\n","protected":false},"author":1,"featured_media":982,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[76],"tags":[],"class_list":["post-980","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kubernetes"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/980","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=980"}],"version-history":[{"count":6,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/980\/revisions"}],"predecessor-version":[{"id":989,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/980\/revisions\/989"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media\/982"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=980"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=980"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=980"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}