Kustomize can encrypt secrets using sops, but it’s not just a simple find-and-replace; it’s about managing your secrets lifecycle within your Kubernetes manifests themselves, blurring the lines between configuration and sensitive data.

Let’s see Kustomize and sops in action. Imagine you have a secret.yaml file:

apiVersion: v1
kind: Secret
metadata:
  name: my-api-key
type: Opaque
data:
  api-key: c29tZXRoaW5nc2hlcmU= # "somethinghere" base64 encoded

And you want to encrypt the api-key value. First, you need to set up sops. This typically involves generating a KMS key (e.g., AWS KMS, GCP KMS, Azure Key Vault) or using a PGP key. For this example, let’s assume you’re using AWS KMS.

You’ll install sops and configure it to use your KMS key. A common way to do this is via a .sops.yaml file in your project root:

creation_rules:
  - kms: arn:aws:kms:us-east-1:123456789012:key/your-kms-key-id

Now, you can encrypt your secret:

sops --encrypt --in-place secret.yaml

The secret.yaml file will transform into something like this:

apiVersion: v1
kind: Secret
metadata:
  name: my-api-key
  sops:
    kms:
    - arn: arn:aws:kms:us-east-1:123456789012:key/your-kms-key-id
    ... # other KMS metadata
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.7.3
sops:
  data:
    api-key: Agc3eH6J... # encrypted value
  lastModified: "2023-10-27T10:00:00Z"
  mac: ...
  pgp: []
  unencrypted_suffix: _unencrypted
  version: 3.7.3

Notice the sops block and the encrypted data.api-key. This encrypted file can be committed to your Git repository.

To integrate this with Kustomize, you’ll create a kustomization.yaml file. The key here is Kustomize’s ability to patch resources. We’ll use a SecretGenerator to create a secret from an encrypted file, and then a StrategicMergePatch to inject the decrypted secret data into a template.

Here’s a kustomization.yaml:

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

resources:
  - base-deployment.yaml # a deployment that uses our secret

secretGenerator:
  - name: my-api-key-secret
    files:
      - api-key=secret.yaml # sops will decrypt this on the fly
    options:
      disableNameSuffixHash: true # Important for predictable names

patchesStrategicMerge:
  - patch-secret-ref.yaml

And patch-secret-ref.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  template:
    spec:
      containers:
        - name: my-app-container
          env:
            - name: API_KEY
              valueFrom:
                secretKeyRef:
                  name: my-api-key-secret # This name comes from secretGenerator
                  key: api-key

When you run kustomize build ., Kustomize invokes sops to decrypt secret.yaml before it generates the final manifest. The secretGenerator directive api-key=secret.yaml tells Kustomize to read the api-key field from the decrypted secret.yaml and use it as the value for the api-key key in the generated secret named my-api-key-secret.

The patchesStrategicMerge then takes your base-deployment.yaml (which references my-api-key-secret) and ensures the environment variable API_KEY is correctly set. The final output will be a deployment manifest that references a Kubernetes Secret named my-api-key-secret, whose api-key data field is populated with the decrypted value.

The problem this solves is managing secrets securely in Git. Instead of committing plaintext secrets, you commit encrypted ones. Your CI/CD pipeline, running with appropriate permissions to decrypt the secrets (e.g., access to the KMS key), can then build and deploy your applications.

Internally, Kustomize’s SecretGenerator is aware of sops if sops is installed in the environment. When it encounters a file specified in secretGenerator that is encrypted by sops, it automatically calls sops --decrypt --output - <filename> to get the plaintext data, then uses that data to populate the generated Secret object. The name: my-api-key-secret in secretGenerator becomes the name of the Kubernetes Secret resource that Kustomize will output.

The exact levers you control are the creation_rules in your .sops.yaml (which keys/methods are used for encryption), the files directive in secretGenerator (mapping which key in the encrypted file becomes which key in the Kubernetes secret), and the patchesStrategicMerge or patchesJson6902 in your kustomization.yaml to reference the generated secret in your application deployments.

A common point of confusion is how the secretGenerator name (my-api-key-secret in the example) relates to the original filename (secret.yaml). The secretGenerator defines the output Kubernetes Secret resource’s name, while the files section maps data from the decrypted source file into that output secret. The name in secretGenerator is what your application manifests will reference.

The next logical step is to explore how to manage different environments (dev, staging, prod) with varying secrets using Kustomize overlays and sops.

Want structured learning?

Take the full Kustomize course →