Back to Distributed Systems & Kubernetes Series

External Secrets Track Part 2: PushSecret & Generators

June 6, 2026 Wasil Zafar 30 min read

Beyond pulling secrets into Kubernetes, ESO can push secrets out to external providers, generate dynamic values without any external store, sync a single secret across multiple namespaces, and transform secret data with templates. These advanced features complete your secrets management strategy.

Table of Contents

  1. PushSecret CRD
  2. Generators
  3. ClusterExternalSecret
  4. Templating & Data Transformation
  5. Exercises
  6. Key Takeaways

PushSecret CRD

PushSecret is the reverse of ExternalSecret — it pushes a Kubernetes Secret to an external provider. Use cases include backing up cluster-generated secrets, syncing secrets across clouds, and centralizing secrets created by operators.

# PushSecret: Push a K8s Secret to AWS Secrets Manager
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: push-db-creds-to-aws
  namespace: production
spec:
  # Refresh interval for re-pushing (catches local updates)
  refreshInterval: 1h

  # Reference to the SecretStore with write permissions
  secretStoreRefs:
    - name: aws-secretsmanager
      kind: SecretStore

  # The Kubernetes Secret to push
  selector:
    secret:
      name: generated-db-password

  # Mapping: which keys to push and where
  data:
    - match:
        secretKey: password           # Key in K8s Secret
        remoteRef:
          remoteKey: production/db-backup   # Path in AWS SM
          property: password                # JSON property to set
# PushSecret with multiple keys and deletion policy
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: push-all-app-secrets
  namespace: production
spec:
  refreshInterval: 30m
  deletionPolicy: Delete    # Delete remote secret when PushSecret is deleted
  secretStoreRefs:
    - name: vault-backend
      kind: SecretStore
  selector:
    secret:
      name: app-config
  data:
    - match:
        secretKey: api-key
        remoteRef:
          remoteKey: production/app/api-key
    - match:
        secretKey: db-url
        remoteRef:
          remoteKey: production/app/database-url
    - match:
        secretKey: redis-url
        remoteRef:
          remoteKey: production/app/redis-url
Use Cases for PushSecret: (1) Backup operator-generated secrets (e.g., cert-manager TLS certs) to Vault for disaster recovery. (2) Cross-cloud sync — push AWS secrets to GCP for multi-cloud workloads. (3) Centralize secrets created by Helm charts into your canonical secret store.

Generators

Generators create dynamic secret values without an external provider. ESO generates the value and stores it in a Kubernetes Secret. Useful for bootstrap passwords, correlation IDs, and short-lived tokens.

Password Generator

# Generate a random password and store it as a K8s Secret
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
metadata:
  name: db-password-gen
  namespace: production
spec:
  length: 32
  digits: 6
  symbols: 4
  symbolCharacters: "!@#$%^&*"
  noUpper: false
  allowRepeat: true
---
# ExternalSecret that uses the generator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: generated-db-password
  namespace: production
spec:
  refreshInterval: 0        # Never refresh (generate once)
  target:
    name: db-password
    creationPolicy: Owner
  dataFrom:
    - sourceRef:
        generatorRef:
          apiVersion: generators.external-secrets.io/v1alpha1
          kind: Password
          name: db-password-gen

UUID Generator

# Generate a UUID v4 for service identification
apiVersion: generators.external-secrets.io/v1alpha1
kind: UUID
metadata:
  name: service-id-gen
  namespace: production
spec: {}
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: service-identity
  namespace: production
spec:
  refreshInterval: 0
  target:
    name: service-id
    creationPolicy: Owner
  dataFrom:
    - sourceRef:
        generatorRef:
          apiVersion: generators.external-secrets.io/v1alpha1
          kind: UUID
          name: service-id-gen

ECR Token Generator

# Generate ECR pull credentials (auto-refreshes before expiry)
apiVersion: generators.external-secrets.io/v1alpha1
kind: ECRAuthorizationToken
metadata:
  name: ecr-token
  namespace: production
spec:
  region: us-east-1
  auth:
    secretRef:
      accessKeyIDSecretRef:
        name: aws-credentials
        key: access-key-id
      secretAccessKeySecretRef:
        name: aws-credentials
        key: secret-access-key
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: ecr-pull-secret
  namespace: production
spec:
  refreshInterval: 6h       # ECR tokens expire in 12h, refresh at 6h
  target:
    name: ecr-registry-credentials
    creationPolicy: Owner
    template:
      type: kubernetes.io/dockerconfigjson
      data:
        .dockerconfigjson: |
          {"auths":{"{{ .registry }}":{"username":"AWS","password":"{{ .token }}"}}}}
  dataFrom:
    - sourceRef:
        generatorRef:
          apiVersion: generators.external-secrets.io/v1alpha1
          kind: ECRAuthorizationToken
          name: ecr-token
Generator Lifecycle: Set refreshInterval: 0 for one-time generation (passwords, UUIDs). Use a non-zero interval for tokens that expire (ECR auth tokens, OAuth tokens). The generated value is stored in the K8s Secret and persists across pod restarts.

ClusterExternalSecret

ClusterExternalSecret syncs the same external secret into multiple namespaces simultaneously. Useful for shared configuration (TLS certs, registry credentials, common API keys) that every namespace needs.

# Sync a shared secret to all namespaces with label env=production
apiVersion: external-secrets.io/v1beta1
kind: ClusterExternalSecret
metadata:
  name: shared-registry-creds
spec:
  # Target namespaces by label selector
  namespaceSelector:
    matchLabels:
      env: production

  # The ExternalSecret template to create in each namespace
  externalSecretSpec:
    refreshInterval: 6h
    secretStoreRef:
      name: central-vault
      kind: ClusterSecretStore
    target:
      name: registry-credentials
      creationPolicy: Owner
      template:
        type: kubernetes.io/dockerconfigjson
    data:
      - secretKey: .dockerconfigjson
        remoteRef:
          key: shared/registry-auth
          property: dockerconfig
# Verify ClusterExternalSecret created secrets in multiple namespaces
kubectl get externalsecrets --all-namespaces | grep shared-registry
# production     shared-registry-creds   central-vault   6h    SecretSynced   True
# staging        shared-registry-creds   central-vault   6h    SecretSynced   True
# monitoring     shared-registry-creds   central-vault   6h    SecretSynced   True

# New namespaces with the label automatically get the secret
kubectl create namespace new-service
kubectl label namespace new-service env=production
# After reconciliation:
kubectl get secret registry-credentials -n new-service
# NAME                     TYPE                             DATA   AGE
# registry-credentials     kubernetes.io/dockerconfigjson   1      30s

Templating & Data Transformation

ESO supports Go templates in spec.target.template to transform secret data before creating the Kubernetes Secret. This enables constructing connection strings, encoding formats, and merging multiple remote values.

# Template: construct a database connection string from individual fields
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-connection
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-sm
    kind: SecretStore
  target:
    name: database-url
    template:
      engineVersion: v2
      data:
        # Construct full connection string from components
        DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@{{ .host }}:{{ .port }}/{{ .dbname }}?sslmode=require"
        # Base64 decode a value from the provider
        TLS_CERT: "{{ .cert | b64dec }}"
        # JSON config file from multiple secrets
        config.json: |
          {
            "database": {
              "host": "{{ .host }}",
              "port": {{ .port }},
              "username": "{{ .username }}",
              "ssl": true
            }
          }
  data:
    - secretKey: username
      remoteRef:
        key: production/rds
        property: username
    - secretKey: password
      remoteRef:
        key: production/rds
        property: password
    - secretKey: host
      remoteRef:
        key: production/rds
        property: host
    - secretKey: port
      remoteRef:
        key: production/rds
        property: port
    - secretKey: dbname
      remoteRef:
        key: production/rds
        property: database
    - secretKey: cert
      remoteRef:
        key: production/rds-tls
        property: ca_cert_b64
# Template: create a .env file from multiple secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-env-file
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-kv
    kind: SecretStore
  target:
    name: app-dotenv
    template:
      engineVersion: v2
      data:
        .env: |
          # Auto-generated by External Secrets Operator
          DB_HOST={{ .db_host }}
          DB_PASSWORD={{ .db_password }}
          REDIS_URL={{ .redis_url }}
          API_KEY={{ .api_key }}
          JWT_SECRET={{ .jwt_secret }}
  data:
    - secretKey: db_host
      remoteRef:
        key: production/database
        property: host
    - secretKey: db_password
      remoteRef:
        key: production/database
        property: password
    - secretKey: redis_url
      remoteRef:
        key: production/redis
        property: url
    - secretKey: api_key
      remoteRef:
        key: production/api
        property: key
    - secretKey: jwt_secret
      remoteRef:
        key: production/auth
        property: jwt_signing_key
Template Functions: ESO templates support Go template functions including b64enc/b64dec (base64), upper/lower (case), trim, replace, toJson/fromJson, and Sprig functions. This enables complex transformations without custom scripts.

Exercises

Exercise 1: Create a Password generator that produces a 24-character password with 4 digits and 2 symbols. Use an ExternalSecret with refreshInterval: 0 to generate it once. Then create a PushSecret that pushes this generated password to a Vault KV path (or use the fake provider for testing). Verify the value exists in both the K8s Secret and the external store.
Exercise 2: Create a ClusterExternalSecret that syncs a TLS certificate from Vault into all namespaces labeled needs-tls=true. Create three namespaces with the label and verify each gets its own copy. Then remove the label from one namespace and verify the secret is cleaned up.
Exercise 3: Use ESO templating to construct a complete PostgreSQL connection string from individual secret fields (host, port, username, password, database). The template should produce a DATABASE_URL key in the format postgresql://user:pass@host:port/db?sslmode=require. Mount the resulting secret as an environment variable in a pod and verify the connection string is correctly assembled.

Key Takeaways

  • PushSecret pushes K8s Secrets to external providers — backup, cross-cloud sync, and centralization
  • Generators create dynamic values (passwords, UUIDs, ECR tokens) without requiring an external store
  • ClusterExternalSecret syncs one secret to multiple namespaces via label selectors — DRY secret management
  • Templating transforms secret data with Go templates — construct connection strings, encode/decode, merge values
  • Use refreshInterval: 0 for one-time generation; non-zero for tokens that expire
  • ESO + Vault + Generators provides a complete secrets lifecycle: generate → store → sync → rotate → push