Use SOPS-backed secrets
Use a SOPS-backed Secret when Kix should own the Kubernetes Secret but its
values must remain encrypted in the repository and Nix store. Kix evaluates
the encrypted file into the cluster artifact, then runs sops --decrypt
immediately before it applies the Secret.
This guide uses an Age identity. Install
SOPS and Age first, and make sure the
machine that runs kix deploy can access the identity.
Create an Age identity
Section titled “Create an Age identity”Generate an identity and print its recipient:
❱ age-keygen -o age-key.txt❱ age-keygen -y age-key.txt Keep age-key.txt outside the repository. Your deployment environment needs
the identity, while developers only need the recipient to encrypt changes.
Encrypt the values
Section titled “Encrypt the values”Create the file with the keys your package expects:
username: application-userpassword: replace-with-a-generated-passwordEncrypt it in place. The shell variable keeps the full recipient out of later commands:
❱ recipient=$(age-keygen -y age-key.txt)❱ sops encrypt --age "$recipient" --in-place credentials.enc.yaml Open the file before committing it and confirm that the values have become
ENC[...] entries. Commit the encrypted file, but not age-key.txt.
Declare and consume the Secret
Section titled “Declare and consume the Secret”Pass the encrypted file to scope.mkSecret and declare its keys explicitly:
credentials = scope.mkSecret { name = "${scope.instanceName}-credentials"; keys = [ "username" "password" ]; source = ./sops-credentials.enc.yaml; };Kix cannot derive the key names from encrypted values, so keys is required.
It also lets the out helpers catch misspelled keys during evaluation.
This validation checks how the package uses the declared keys, not whether the
encrypted file contains them. If the file omits a declared key, Kix can apply
the Secret successfully and the workload fails when it tries to read the key.
By contrast, the deploy-time readiness check for scope.mkSecretRef verifies
that every declared key exists in the live Secret.
Add hash to catch an encrypted file that changed without the declaration
changing:
credentials = scope.mkSecret { name = "credentials"; source = ./credentials.enc.yaml; keys = [ "username" "password" ]; hash = "sha256-<base64>";};Kix records this value in the kix.run/content-hash annotation. During
deployment, it hashes the decrypted data and stops if the hash differs. On the
first attempt, use the computed hash from the error message to fill in the
declaration.
Consume the Secret in the same way as any other Kix-managed Secret:
deployment = scope.mkDeployment { name = scope.instanceName; spec = { replicas = 1; selector.matchLabels = scope.selectorLabels; template.spec.containers = [ { name = "app"; image = "busybox:1.36"; command = [ "sh" "-c" "sleep 3600" ]; env = self.credentials.out.mkEnv { APP_USERNAME = "username"; APP_PASSWORD = "password"; }; } ]; }; };The reference from deployment to credentials also gives Kix the deploy
ordering between them.
Check the cluster artifact
Section titled “Check the cluster artifact”Render the cluster before deploying:
❱ kix build how-to-platform-sops --output json
[
{
"kind": "Secret",
"namespace": "sops-example",
"name": "app-credentials",
"declaredKeys": [
"username",
"password"
],
"plaintextIncluded": false
}
] The output excerpt reports the declared keys and confirms that the rendered
Secret has neither data nor stringData. The artifact contains a reference
to the encrypted source, not the decrypted values.
Deploy with access to the identity
Section titled “Deploy with access to the identity”Point SOPS at the identity on the deployment machine, then deploy normally:
❱ export SOPS_AGE_KEY_FILE=/secure/path/age-key.txt❱ kix deploy how-to-platform-sops Kix stops the deploy if SOPS is unavailable, the identity cannot decrypt the file, or the decrypted document is not a top-level key-value map.
Check that Kubernetes received the two declared keys without printing their values:
❱ kubectl get secret app-credentials -n sops-example
NAME TYPE DATA AGE
app-credentials Opaque 2 14s Give the deployment identity to CI through its secret store or workload identity mechanism. If another controller should own the Kubernetes Secret, use an externally managed Secret instead.