Skip to main content
SOPS (Secrets OPerationS) encryption allows you to securely store sensitive values like passwords, API keys, and credentials in your GitOps repository. Encrypted values are automatically decrypted during deployment.

What is SOPS?

SOPS is an editor for encrypted files that supports YAML, JSON, ENV, INI and BINARY formats. Ankra uses SOPS with AGE encryption to protect sensitive values in your Stack configurations.

How It Works

  1. In the Stack Builder: You enter the value in plaintext and list its key under Encrypted Keys (SOPS)
  2. In Ankra: The control plane stores the value as you entered it, together with the list of keys to seal. Listing a key does not encrypt the value inside Ankra
  3. On every push to Git: Ankra seals the listed keys with your organisation’s AGE public key, so your repository only ever receives ENC[AES256_GCM,...] for them
  4. On Deploy: The Ankra agent decrypts inside your cluster - directly with sops on the native engine, or through ArgoCD’s helm-secrets plugin on the ArgoCD engine (see Deployment Engines)
Because sealing happens at the push, the Stack Builder shows the value you typed, not ciphertext, including after you save and reopen it. That is expected. What protects the value is that it is never written to Git unsealed: a push that cannot seal a listed key is refused instead of committed. To confirm a value is sealed, look at the file in your Git repository.

Getting Started

Setting up SOPS encryption is a single step. Once initialized, encryption is enabled by default and cluster decryption is handled automatically.
1

Navigate to Encryption Settings

Go to Organisation Settings → Encryption.
2

Initialize Encryption

Click Initialize Encryption. This generates an AGE key pair:
  • Public key: Used to encrypt values (visible to you)
  • Private key: Used to decrypt values (stored securely in Ankra’s secret store)
That’s it. After initialization:
  • Encryption is enabled by default - fields you mark as encrypted will be protected immediately.
  • Cluster decryption is automatic - on the native engine the agent requests the key from Ankra when it deploys a sealed value, and on the ArgoCD engine Ankra installs the key into the cluster for helm-secrets. There is nothing to configure per-cluster.
The AGE public key is displayed on the Encryption settings page. You can copy it for use with external SOPS tools if needed. The public key looks like: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Encrypting Values in Stacks

Using the Stack Builder

When editing a manifest or add-on values in the Stack Builder:
  1. Look for the Encrypted Keys (SOPS) section
  2. Add key names that should be encrypted (e.g., password, apiKey, token)
  3. Enter the plaintext value as normal
  4. Save. Ankra seals those specific keys every time it writes the file to your Git repository
Only the specified keys are encrypted. Other values remain in plaintext for easy review and debugging.

Common Keys to Encrypt

Example: Encrypting a Database Password

Before encryption (what you see in the editor):
After encryption (what’s stored in Git):

Example: Encrypting a Registry Pull Secret (.dockerconfigjson)

A kubernetes.io/dockerconfigjson Secret stores its payload under a key whose name literally begins with a dot: .dockerconfigjson. Add that key, leading dot included, to the Encrypted Keys (SOPS) list (or pass --key .dockerconfigjson to the CLI). The leading dot marks it as a literal key name rather than a nested dotted path, so it is matched and encrypted as-is.

Encrypting Helm Values

For add-ons (Helm charts), you can encrypt specific values:
  1. Open the add-on in the Stack Builder
  2. In the values editor, find the Encrypted Keys (SOPS) section
  3. Add paths to sensitive values

Helm Values Path Examples

Example: Encrypting Grafana admin password
Add adminPassword to the encrypted keys list.

What an Entry Actually Matches

Dot notation tells Ankra which key you mean; it does not confine the encryption to that one place in the file. Ankra compiles the entries into a SOPS encrypted_regex built from the key names, and SOPS applies that regex to every key at every depth of the document. A dotted entry contributes its last segment as one of those names. So an entry auth.password names the key password, and every key called password in that file is sealed - auth.password, backups.password, and any other. That is usually what you want, and it is why a key added under a new section next month is sealed without changing the declaration. It becomes a problem when the key name is not unique to your secret.
Do not mark a key whose name is a structural YAML or Kubernetes field, such as key, name, path, value, type, host, port or operator. The entry seals every field of that name in the file, including the ones the chart needs to read in plaintext.This applies to dotted entries too, by their last segment. tls.key, metadata.name and spec.host end in a structural field name, so they seal every key, name and host in the document exactly as the bare entry would. Writing the parent section does not scope the seal to it.Marking a key called key in a kube-prometheus-stack values file produces encrypted_regex: ^(key)$, which seals the node-affinity term as well:
Marking tls.key on the same file produces ^(key|tls\.key)$ — a different regex, but it still carries key as an alternative, so it seals that same affinity term.The rendered chart then fails, and every GitOps sync of that cluster fails to decrypt with Input string kubernetes.io/hostname does not match sops data format, until the file is decrypted again.Give the value a distinctive key name instead (opsgenieApiKey, smtpPassword), or seal the whole credential as its own Secret manifest. Renaming the parent section does not help; only the last segment decides what is matched.

Encrypting Keys by Pattern

An entry in the Encrypted Keys (SOPS) list is normally a literal key name, matched exactly. Prefix an entry with glob: to seal every key whose name matches a pattern instead - including keys added to the document later:

Pattern Rules

  • glob: is the opt-in. Entries without the prefix keep their exact-match meaning, so a literal key name that contains a dot (cloud.conf, .dockerconfigjson) is never reinterpreted as a pattern.
  • Only * is a wildcard. It matches any run of characters, including none. Every other character is literal.
  • A pattern describes a key name, not a path. SOPS matches key names at every depth of the document, so glob:DB_* seals a top-level DB_PASSWORD and a nested backups.DB_PASSWORD alike.
  • A leading data. or stringData. section is accepted and stripped, the same as for a literal entry: glob:stringData.DB_* and glob:DB_* select the same keys.
  • The entry is stored as written. encrypted_paths keeps glob:stringData.DB_* verbatim, and Ankra re-expands it into the SOPS encrypted_regex on every push. A key added to the document next month is sealed by the same rule, with no change to the declaration - and because the rule lives in the SOPS metadata, a key you seal by hand with the sops editor is matched the same way.
Example: Sealing every DB_ key of a Secret with glob:stringData.DB_*

Rejected Patterns

A pattern that cannot mean what was intended is rejected when you save, with an error naming the entry and the reason (over the API this is an HTTP 422, for example Invalid encrypted_paths entry "glob:password": contains no * wildcard; declare an exact key name without the glob: prefix): A valid pattern that matches zero keys fails later, at encrypt or push time, exactly like a misspelled literal key: rather than write a file that only looks encrypted, Ankra refuses with an error such as declared encrypted_paths [glob:DB_*] match no key in this document, so sops encrypted nothing. Fix the pattern (or add the keys it should match) and save again.

An Empty or Absent Declaration

What keeps a file sealed on a platform write is its declaration. Ankra seals the file from encrypted_paths on every push it makes, and a manifest or add-on with no entries is written as plain content.
  • No entries, no checks. Every check on a declaration runs on the entries it carries. The store-time guard, the match no key in this document refusal and the over-match warning ankra cluster validate reports (category encrypted_paths_over_match) are all skipped when there are none; only plaintext-secret detection still runs, on the content itself. Silence from those checks on a resource with an empty declaration is not evidence that its file is sealed.
  • Over the API, [] and an absent key mean different things. On an update, omitting encrypted_paths keeps the stored declaration; sending encrypted_paths: [] replaces it with nothing. If the file is sealed in your repository, the next platform write renders it as plain content and overwrites the sealed copy. Send the key only when you mean to set the declaration.
  • The CLI drops an empty list. ankra cluster apply sends encrypted_paths only when it has entries, so [] in a cluster YAML applied with the CLI behaves like an absent key and keeps whatever is stored. A sealed from_file is sent as it is on disk; when no declaration is stored for it, the apply is refused with content is SOPS-encrypted but encrypted_paths is empty.
  • In a Git repository Ankra syncs, the file wins. When Ankra reads the cluster directory, the declaration for a sealed file is derived from its sops metadata and replaces whatever the cluster YAML declares; the cluster YAML is rewritten with the derived entries on the next platform write. The cluster YAML Ankra renders lists encrypted_paths: [] for every add-on and manifest without a declaration, so that line on its own only says that nothing is declared.

Using the CLI

You can also encrypt and decrypt values using the Ankra CLI for local GitOps workflows.

Encrypting Manifest Values

This will:
  1. Find the manifest in the cluster YAML
  2. Read the referenced manifest file
  3. Encrypt the specified key using your organisation’s SOPS key
  4. Update the manifest file with encrypted values
  5. Add the key to encrypted_paths in the cluster YAML

Encrypting Addon Values

Encrypting Keys by Pattern with the CLI

--key also accepts the glob: pattern form described in Encrypting Keys by Pattern:
The pattern is recorded in encrypted_paths as written, so keys added later are sealed automatically the next time the values are encrypted. A pattern that matches no key fails the same way a misspelled exact key does. See ankra cluster encrypt for the full flag reference.

Decrypting for Inspection

To view the decrypted contents of a manifest file:
The decrypt command prints to stdout, so you can pipe it to other tools or redirect to a file if needed.

Adding Keys to Existing Encrypted Files

You can add new encrypted keys to files that are already SOPS-encrypted:
The CLI will automatically decrypt the file, merge the encrypted paths, and re-encrypt with all keys protected.
This only works if the file was encrypted with your organisation’s SOPS key. If the file was encrypted by a different organisation, you’ll see an error message and need to decrypt it first using the original key.
For more CLI commands, see the Ankra CLI documentation.

How Decryption Works

Your organisation’s AGE private key is kept in Ankra’s secret store. How a cluster uses it depends on the deployment engine of the add-on or manifest being deployed.

Native engine

The Ankra agent decrypts sealed values itself. When a deploy reaches a sealed file, the agent requests the organisation’s private key from Ankra, runs sops --decrypt inside your cluster, and hands the result to Helm or applies the manifest. Nothing is installed in the cluster beforehand and there is no per-cluster setup. This is the default engine for clusters created since May 2026.

ArgoCD engine

On clusters that use ArgoCD, Ankra configures ArgoCD to decrypt during Helm template rendering:
  1. Deploys the AGE private key as a Kubernetes Secret in the ArgoCD namespace
  2. Configures helm-secrets plugin on the ArgoCD repo-server
  3. Sets up automatic decryption during Helm template rendering
The helm-secrets plugin is automatically configured with:
  • HELM_SECRETS_BACKEND=sops - Use SOPS for decryption
  • SOPS_AGE_KEY_FILE=/ankra-sops-age-key/age.agekey - Path to the private key
  • Support for secrets:// value file scheme

Troubleshooting

  1. Verify SOPS is initialized at the organisation level (Organisation Settings → Encryption)
  2. Ensure the content was encrypted with the current organisation key (compare sops.age.recipient in the file with the public key on the Encryption settings page)
  3. On the ArgoCD engine only, confirm the cluster has ArgoCD installed and that the repo-server has restarted:
On the native engine the failing step’s own message in the deployment view names the file and the reason.
On the native engine the agent decrypts on its own and needs nothing installed in the cluster, so check the first entry above instead.An add-on that uses the ArgoCD engine needs ArgoCD to decrypt. If ArgoCD is not installed:
  1. Create a Stack with the ArgoCD add-on
  2. Deploy and wait for ArgoCD to become ready
  3. The SOPS decryption key will be deployed automatically
  1. Look in your Git repository, not in the Stack Builder. The Stack Builder always shows the value you entered; sealing happens when Ankra writes the file to Git
  2. Check that the key name matches exactly (case-sensitive)
  3. Verify SOPS is initialized at the organisation level
  4. Ensure the key is added to the Encrypted Keys list before saving
Name a top-level key directly, or use dot notation to locate a nested one. Either way the entry names a key, and every key of that name in the document is sealed - see What an Entry Actually Matches.
An entry seals every key of that name at every depth, so an entry named after a structural field (key, name, path, value, type, host, port, operator) also seals the chart’s own fields. Check dotted entries by their last segment: tls.key and metadata.name are the same hazard as a bare key or name. The symptom is a render or sync failure naming a value that was never a secret, such as Input string kubernetes.io/hostname does not match sops data format.Decrypt the file, remove the over-broad entry from Encrypted Keys (SOPS), give the secret a distinctive key name, and re-encrypt. See What an Entry Actually Matches.If that entry is the last encrypted_paths entry anywhere on the cluster, removing it also stops Ankra rendering the cluster’s .sops.yaml, and the next platform write prunes the file along with any creation rules you keep there by hand. Keep a copy first - see Merging the SOPS configuration file.
A glob: pattern entry the grammar rejects fails the save with Invalid encrypted_paths entry "...": <reason>. The reason names the fix:
  • contains no * wildcard - the pattern has no *; declare the exact key name without the glob: prefix
  • would match every key in the document - the pattern is only wildcards; keep at least one literal character
  • must be followed by a key-name pattern or names a section but no key-name pattern - add the pattern after the prefix or section, such as glob:stringData.DB_*
An error saying declared paths match no key in this document means every entry (literal or pattern) selected nothing - check the spelling against the document’s actual key names. See Encrypting Keys by Pattern for the full rules.
An update sent over the API with encrypted_paths: [] clears the stored declaration, and the next platform write renders the file as plain content over the sealed copy. Restore the declaration (declare the keys again, or re-encrypt with ankra cluster encrypt) and rotate any value that reached the repository in plaintext. On updates, omit the key to keep the stored declaration - see An Empty or Absent Declaration.

AI Prompts

Press ⌘+J to open the AI Assistant and use these prompts:

Advanced Configuration

The defaults described above work for most setups. The following sections cover optional configuration that you may need if you want to change the default behaviour.

Disabling Organisation Encryption

SOPS encryption is enabled by default after initialization. If you need to temporarily stop encrypting new values (existing encrypted content remains encrypted):
  1. Go to Organisation Settings → Encryption
  2. Toggle SOPS Encryption off
While it is off, Ankra does not write a file with listed keys to Git unsealed: a push of that file is refused until encryption is re-enabled. Re-enable at any time to resume encryption. This does not affect clusters’ ability to decrypt already-encrypted values.

Per-Cluster Decryption Toggle

On the ArgoCD engine, Ankra automatically deploys the SOPS decryption key to every cluster that has ArgoCD. If you need to manage this manually for a specific cluster:
  1. Go to your cluster → Settings → Encryption
  2. Toggle Enable SOPS Decryption on or off
Disabling cluster decryption will cause deployments with encrypted values to fail on that cluster. Only disable this if the cluster should not process encrypted Stacks.

Key Rotation

Key rotation is not available as a self-service control in the portal or the CLI today. Rotating the organisation key is more than generating a new pair: every file already sealed in your Git repositories has to be re-encrypted with the new public key before the old private key is retired, or those files can no longer be decrypted at deploy time.

Stacks

Build and deploy reusable Stack configurations.

GitOps

Sync Stacks with Git repositories.

Cloudflare Tunnel

Encrypt tunnel tokens with SOPS.

Manifests

Create custom Kubernetes resources with encryption.