Generating Kubernetes Secrets with Kustomize secretGenerator

0


Kustomize generators are one of the most practical features for managing environment-specific Kubernetes configuration. In the previous article, we looked at configMapGenerator. In this article, we will focus on secretGenerator.

The behavior is very similar: Kustomize reads input values from files, .env files, or literals and generates a Kubernetes object during the build step. The difference is that secretGenerator creates a Secret instead of a ConfigMap..

This is useful when the generated data contains sensitive values such as database usernames, database passwords, API tokens, or private keys.

Kubernetes Secrets are intended for confidential data, while ConfigMaps are intended for non-confidential configuration.

What secretGenerator Does

secretGenerator entry tells Kustomize to generate a Kubernetes Secret as part of the final manifest output.

Instead of manually writing a Secret manifest like this:

apiVersion: v1
kind: Secret
metadata:
  name: local-config
type: Opaque
data:
  DB_USERNAME: bG9jYWxfZGI=
  DB_PASSWORD: bG9jYWxfcGFzc3dvcmQ=

you can keep local values in a file and let Kustomize generate the final Secret:

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

Kustomize will read .env.local, create a Kubernetes Secret, base64-encode the values, and optionally append a hash suffix to the generated Secret name.

Example Project Structure

For a development overlay, the structure can look like this:

k8s/
├── base/
│   ├── deployment.yaml
│   └── kustomization.yaml
└── overlays/
    └── dev/
        ├── .env.local
        ├── db-init.js
        ├── nginx-deployment.yaml
        └── kustomization.yaml

The .env.local file is intentionally placed inside the local development overlay because it contains values that are only needed for local development.

Local Secret Values in an .env File

A simple .env.local file can contain the database host, username, and password:

DB_HOST=postgres.dev.svc.cluster.local
DB_USERNAME=local_db
DB_PASSWORD=local_password

This file should normally not be committed to the repository when it contains real credentials.

Add it to .gitignore:

.env.local
.env.*.local

For a course demo, local placeholder values are fine. For real environments, especially staging and production, avoid committing secret material to Git.

Creating a Secret from an .env File

Inside overlays/dev/kustomization.yaml, we can define the generator:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: dev
namePrefix: dev-
nameSuffix: -api

resources:
  - nginx-deployment.yaml

labels:
  - includeSelectors: true
    pairs:
      app.kubernetes.io/part-of: kustomize-demo
      app.kubernetes.io/environment: dev

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

The important parts are:

FieldPurpose
secretGeneratorDefines one or more Secrets that Kustomize should generate.
nameThe logical Secret name used inside the kustomization.
typeThe Kubernetes Secret type. Opaque is used for generic key-value secrets.
envsReads key-value pairs from one or more .env files.

When Kustomize builds the overlay, it generates a Secret similar to this:

apiVersion: v1
data:
  DB_HOST: cG9zdGdyZXMuZGV2LnN2Yy5jbHVzdGVyLmxvY2Fs
  DB_PASSWORD: bG9jYWxfcGFzc3dvcmQ=
  DB_USERNAME: bG9jYWxfZGI=
kind: Secret
metadata:
  name: dev-local-config-api-7c9k8m6h5f
  namespace: dev
type: Opaque

The exact hash suffix will depend on the generated data.

You Do Not Need to Base64-Encode Values Yourself

When you use secretGenerator, you provide the values as normal text in the source file:

DB_USERNAME=local_db
DB_PASSWORD=local_password

Kustomize generates the Secret and encodes the values into the data field of the Secret manifest.

This is easier and less error-prone than manually encoding each value yourself.

However, base64 encoding is not encryption. Anyone who can read the Secret manifest or retrieve the Secret from the Kubernetes API can decode the value.

For example:

echo 'bG9jYWxfcGFzc3dvcmQ=' | base64 --decode

Output:

local_password

So, while Secret is the correct Kubernetes object for sensitive data, you still need proper security controls around it.

The Default Hash Suffix

By default, generated Secrets and ConfigMaps receive a hash suffix:

local-config-7c9k8m6h5f

When namePrefix and nameSuffix are also configured, the final name can look like this:

dev-local-config-api-7c9k8m6h5f

The hash is based on the generated data. When the Secret content changes, Kustomize creates a Secret with a new name.

This behavior is important because it helps Kubernetes workloads notice that the configuration changed. When a Deployment references a generated Secret and Kustomize updates that reference to a new name, the Pod template changes. That Pod template change can trigger a rollout.

Disabling the Name Suffix Hash

You can disable the generated hash globally for generators in the current kustomization:

generatorOptions:
  disableNameSuffixHash: true

With this option enabled, the generated Secret name remains stable:

dev-local-config-api

This can be convenient during local development or when another system expects a fixed Secret name.

However, be careful. Stable names also mean that a Secret content change might not automatically trigger a Deployment rollout. If your application reads values only at startup, existing Pods may continue running with the old values until they are restarted.

Use disableNameSuffixHash: true only when you have a clear reason.

Referencing Generated Secrets in a Deployment

A very useful part of Kustomize is that you usually reference the original generator name, not the final generated name.

For example, the generator name is:

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

In the Deployment, reference local-config:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-inside-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-inside-dev
  template:
    metadata:
      labels:
        app: nginx-inside-dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          envFrom:
            - secretRef:
                name: local-config

After running Kustomize, the reference is updated automatically:

envFrom:
  - secretRef:
      name: dev-local-config-api-7c9k8m6h5f

This means you should not hardcode the generated hash in your Deployment. Let Kustomize calculate the final name.

Mounting a Generated Secret as a Volume

Secrets can also be mounted as files inside a container.

Example:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-inside-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-inside-dev
  template:
    metadata:
      labels:
        app: nginx-inside-dev
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          volumeMounts:
            - name: app-secrets
              mountPath: /app/secrets
              readOnly: true
      volumes:
        - name: app-secrets
          secret:
            secretName: local-config

Kustomize understands this standard Kubernetes Secret reference:

secretName: local-config

and transforms it into the generated name:

secretName: dev-local-config-api-7c9k8m6h5f

Referencing Generated ConfigMaps Works the Same Way

The same behavior applies to generated ConfigMaps.

For example, create a ConfigMap from a local JavaScript initialization file:

configMapGenerator:
  - name: db-init-config
    files:
      - db-init.js

Then reference the original generator name in the Deployment:

volumes:
  - name: db-config
    configMap:
      name: db-init-config

After Kustomize builds the overlay, the reference can become:

volumes:
  - name: db-config
    configMap:
      name: dev-db-init-config-api-fbf87h9k6m

This is the key idea: the reference in your source YAML must match the generator name. Kustomize then updates the reference to include the prefix, suffix, and hash.

Why the Resource Must Be Included in resources

Kustomize can only transform resources that are part of the kustomization.

If the Deployment references a generated Secret but the Deployment file is not listed under resources, Kustomize will not process it.

Correct:

resources:
  - nginx-deployment.yaml

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

Incorrect:

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

In the incorrect example, Kustomize generates the Secret, but it never sees the Deployment that references it. Therefore, it cannot update the reference inside the Deployment.

Running the Overlay

To preview the generated YAML:

kubectl kustomize overlays/dev

or:

kustomize build overlays/dev

To apply it to the cluster:

kubectl apply -k overlays/dev

To inspect the generated Secret:

kubectl get secret -n dev

To view the encoded values:

kubectl get secret dev-local-config-api-7c9k8m6h5f -n dev -o yaml

To decode one value:

kubectl get secret dev-local-config-api-7c9k8m6h5f \
  -n dev \
  -o jsonpath='{.data.DB_PASSWORD}' | base64 --decode

Using literalsfiles, and envs

secretGenerator supports different input styles.

Literals

Use literals for small demo values:

secretGenerator:
  - name: database-creds
    type: Opaque
    literals:
      - DB_USERNAME=admin
      - DB_PASSWORD=local-password

This is convenient for examples, but it is not a good idea for real credentials if the kustomization file is committed to Git.

Files

Use files when each file should become a Secret key:

secretGenerator:
  - name: ssh-key
    type: Opaque
    files:
      - id_rsa

The filename becomes the key, and the file content becomes the value.

Environment Files

Use envs when you already have simple key-value pairs:

secretGenerator:
  - name: local-config
    type: Opaque
    envs:
      - .env.local

This is often the cleanest option for local development.

Opaque Secret Type

Opaque is the default generic Secret type in Kubernetes. It is suitable for arbitrary application credentials such as:

  • database usernames
  • database passwords
  • API tokens
  • local development credentials
  • internal application keys

Kubernetes also supports specialized Secret types, such as TLS Secrets and Docker registry Secrets. Use those specialized types when they match the use case. For ordinary application key-value pairs, Opaque is normally the correct choice.

Security Considerations

Using secretGenerator improves the workflow, but it does not remove the need for Secret management discipline.

Do Not Treat Base64 as Encryption

Kubernetes Secret values are base64-encoded in the manifest. That encoding is reversible and should not be treated as a security boundary.

Enable Encryption at Rest

By default, Secret data can be stored unencrypted in the Kubernetes API server backing store unless encryption at rest is configured.

For production clusters, enable encryption at rest for Secrets.

Use Least-Privilege RBAC

Be careful with permissions such as:

get secrets
list secrets
watch secrets

A user or service account that can list Secrets in a namespace can effectively retrieve sensitive data from that namespace.

Avoid Committing Real Secrets to Git

For local demos, .env.local can be useful. For production, prefer a proper Secret management workflow.

Common production approaches include:

  • external Secret stores
  • the Secrets Store CSI Driver
  • External Secrets Operator
  • sealed or encrypted GitOps workflows
  • cloud-native secret managers

Be Careful with Logs and CI Output

Avoid commands that print Secret values in CI logs. Even base64-encoded values can be decoded later.

Common Mistakes

MistakeWhy it is a problemBetter approach
Hardcoding the generated Secret name with the hashThe hash changes when the Secret data changes.Reference the generator name, such as local-config.
Forgetting to list the Deployment under resourcesKustomize cannot update a resource it does not process.Add the Deployment YAML file to resources.
Disabling the hash without a rollout strategyExisting Pods may continue using old values.Keep the hash enabled or restart Pods explicitly.
Treating base64 as secure encryptionBase64 is reversible encoding.Use RBAC, encryption at rest, and external Secret stores where appropriate.
Using secretGenerator literals for production credentials in GitThe credentials become part of the repository history.Use external Secret management or encrypted GitOps tooling.
Expecting custom resources to always update automaticallyKustomize knows common built-in Kubernetes references, but not every custom field.Add transformer configuration or use replacements for custom references.

Important Detail: Built-In Name References

Kustomize has built-in knowledge of many standard Kubernetes reference fields.

For ConfigMaps, this includes fields such as:

configMap:
  name: db-init-config

and:

envFrom:
  - configMapRef:
      name: app-config

For Secrets, this includes fields such as:

secret:
  secretName: local-config

and:

envFrom:
  - secretRef:
      name: local-config

This is why Kustomize can update these references when it adds a prefix, suffix, or hash.

For custom resources, Kustomize may not automatically know which fields are references. In that case, you may need a custom transformer configuration or another mechanism such as replacements.

Final Thoughts

secretGenerator is a clean way to generate Kubernetes Secrets without manually writing base64-encoded Secret manifests.

For local development, it works especially well with .env files that are not committed to the repository. For production, it can still be useful, but it should be combined with a secure Secret management strategy.

The most important practical rule is simple:

Reference the generator name in your manifests, and let Kustomize produce the final name.

That allows Kustomize to correctly apply namePrefixnameSuffix, and the generated hash while keeping your source YAML readable and stable.

References

  • Kubernetes: Declarative Management of Kubernetes Objects Using Kustomize - kubernetes[.]io/docs/tasks/manage-kubernetes-objects/kustomization/
  • Kubernetes: Managing Secrets using Kustomize - kubernetes[.]io/docs/tasks/configmap-secret/managing-secret-using-kustomize/
  • Kubernetes: Secrets - kubernetes[.]io/docs/concepts/configuration/secret/
  • Kubernetes: Good practices for Kubernetes Secrets - kubernetes[.]io/docs/concepts/security/secrets-good-practices/
  • Kustomize: Generator Options - github[.]com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md
  • Kustomize: Transformer Configurations and Name References - github[.]com/kubernetes-sigs/kustomize/blob/master/examples/transformerconfigs/README.md

Post a Comment

0 Comments

Post a Comment (0)

#buttons=(Ok, Go it!) #days=(20)

This site uses cookies from Google to deliver its services and analyze traffic. Your IP address and user-agent are shared with Google along with performance and security metrics to ensure quality of service, generate usage statistics, and to detect and address abuse. More Info
Ok, Go it!