> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ankra.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SOPS Encryption

> Encrypt sensitive values in your Stacks with SOPS for secure GitOps workflows

<Note>
  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.
</Note>

## What is SOPS?

[SOPS](https://github.com/getsops/sops) is an editor for encrypted files that supports YAML, JSON, ENV, INI and BINARY formats. Ankra uses SOPS with [AGE](https://github.com/FiloSottile/age) encryption to protect sensitive values in your Stack configurations.

### How It Works

```mermaid theme={null}
flowchart LR
    Editor[Stack Editor] -->|Encrypt with AGE public key| Git[Git Repo]
    Git -->|Decrypt with AGE private key| Cluster[Cluster]
```

1. **In the Stack Builder**: You enter secrets in plaintext
2. **On Save**: Ankra encrypts marked fields with your organisation's AGE public key
3. **In Git**: Encrypted values appear as `ENC[AES256_GCM,...]` — safe to commit
4. **On Deploy**: ArgoCD's helm-secrets plugin decrypts values using the private key

***

## Getting Started

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

<Steps>
  <Step title="Navigate to Encryption Settings">
    Go to **Organisation Settings** → **Encryption**.
  </Step>

  <Step title="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 vault)
  </Step>
</Steps>

That's it. After initialization:

* **Encryption is enabled** by default — fields you mark as encrypted will be protected immediately.
* **Cluster decryption is automatic** — any cluster with ArgoCD installed receives the decryption key automatically. 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. On save, Ankra encrypts those specific keys

<Tip>
  Only the specified keys are encrypted. Other values remain in plaintext for easy review and debugging.
</Tip>

### Common Keys to Encrypt

| Key Name           | Use Case                                     |
| ------------------ | -------------------------------------------- |
| `password`         | Database passwords, admin credentials        |
| `apiKey`           | Third-party API keys                         |
| `token`            | Authentication tokens, tunnel tokens         |
| `secretKey`        | Encryption keys, signing keys                |
| `connectionString` | Database connection strings with credentials |
| `privateKey`       | SSH keys, TLS private keys                   |

### Example: Encrypting a Database Password

**Before encryption** (what you see in the editor):

```yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
  name: postgres-credentials
  namespace: database
type: Opaque
stringData:
  username: app_user
  password: super-secret-password
```

**After encryption** (what's stored in Git):

```yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
  name: postgres-credentials
  namespace: database
type: Opaque
stringData:
  username: app_user
  password: ENC[AES256_GCM,data:vxvYs7vKw+bPqNWO3tE=,iv:...,tag:...,type:str]
sops:
  age:
    - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
      enc: |
        -----BEGIN AGE ENCRYPTED FILE-----
        YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxVEdI...
        -----END AGE ENCRYPTED FILE-----
  encrypted_regex: ^(password)$
  version: 3.9.4
```

### 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.

```yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
  name: registry-auth
  namespace: platform
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: ENC[AES256_GCM,data:vxvYs7vKw+bPqNWO3tE=,iv:...,tag:...,type:str]
sops:
  age:
    - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
      enc: |
        -----BEGIN AGE ENCRYPTED FILE-----
        YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxVEdI...
        -----END AGE ENCRYPTED FILE-----
  encrypted_regex: ^(\.dockerconfigjson)$
  version: 3.9.4
```

***

## 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

| Path                 | Description                    |
| -------------------- | ------------------------------ |
| `adminPassword`      | Top-level key                  |
| `auth.password`      | Nested key (uses dot notation) |
| `credentials.apiKey` | Deeply nested key              |

**Example**: Encrypting Grafana admin password

```yaml theme={null}
# Values for grafana chart
adminUser: admin
adminPassword: my-grafana-password  # ← This gets encrypted
persistence:
  enabled: true
```

Add `adminPassword` to the encrypted keys list.

***

## Using the CLI

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

### Encrypting Manifest Values

```bash theme={null}
# Encrypt a key in a manifest
ankra cluster encrypt manifest trinity-database-secret --key TRINITY_DB_PASSWORD -f cluster.yaml
```

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

```bash theme={null}
# Encrypt a key in addon configuration
ankra cluster encrypt addon --name grafana --key adminPassword -f cluster.yaml
```

### Decrypting for Inspection

To view the decrypted contents of a manifest file:

```bash theme={null}
# View decrypted manifest content
ankra cluster decrypt manifest trinity-database-secret -f cluster.yaml
```

<Tip>
  The decrypt command prints to stdout, so you can pipe it to other tools or redirect to a file if needed.
</Tip>

### Adding Keys to Existing Encrypted Files

You can add new encrypted keys to files that are already SOPS-encrypted:

```bash theme={null}
# File already has 'password' encrypted, now add 'api_token'
ankra cluster encrypt manifest my-secret --key api_token -f cluster.yaml
```

The CLI will automatically decrypt the file, merge the encrypted paths, and re-encrypt with all keys protected.

<Warning>
  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.
</Warning>

For more CLI commands, see the [Ankra CLI documentation](/integrations/ankra-cli#sops-encryption).

***

## How Decryption Works

When SOPS is initialized for your organisation, Ankra automatically configures decryption on clusters that have ArgoCD:

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

### What Gets Deployed

| Component                   | Namespace          | Purpose                         |
| --------------------------- | ------------------ | ------------------------------- |
| `ankra-sops-age-key` Secret | `argocd`           | Stores the AGE private key      |
| helm-secrets plugin         | ArgoCD repo-server | Enables SOPS decryption in Helm |
| SOPS binary                 | ArgoCD repo-server | Performs the actual decryption  |

### ArgoCD Integration

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

<AccordionGroup>
  <Accordion title="Deployment Fails with Decryption Error">
    1. Verify SOPS is initialized at the organisation level (**Organisation Settings** → **Encryption**)
    2. Confirm the cluster has ArgoCD installed and that the repo-server has restarted
    3. Ensure the content was encrypted with the current organisation key (not a rotated key)

    ```bash theme={null}
    # Check ArgoCD repo-server logs
    kubectl logs -n argocd -l app.kubernetes.io/component=repo-server | grep -i sops
    ```
  </Accordion>

  <Accordion title="Cluster Not Decrypting Encrypted Values">
    The cluster needs ArgoCD to decrypt SOPS values. 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
  </Accordion>

  <Accordion title="Values Not Being Encrypted">
    1. Check that the key name matches exactly (case-sensitive)
    2. Verify SOPS is initialized at the organisation level
    3. Ensure the key is added to the **Encrypted Keys** list before saving

    Keys must be top-level or specified with dot notation for nested paths.
  </Accordion>

  <Accordion title="Key Rotation Stuck">
    If you initiated key rotation but can't complete it:

    1. **Cancel the rotation** if you haven't re-encrypted content yet
    2. If content is already re-encrypted, verify by checking the `sops.age.recipient` field matches the new public key
    3. Click **Confirm Rotation** to finalize
  </Accordion>
</AccordionGroup>

***

## AI Prompts

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

<AccordionGroup>
  <Accordion title="Set Up SOPS for a Stack">
    ```
    I have a Stack with database credentials. Help me encrypt
    the password field with SOPS before pushing to Git.
    ```
  </Accordion>

  <Accordion title="Troubleshoot Decryption Failure">
    ```
    My Stack deployment is failing with a SOPS decryption error.
    Help me troubleshoot.
    ```
  </Accordion>
</AccordionGroup>

***

## 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

When disabled, fields marked for encryption will be saved in plaintext. Re-enable at any time to resume encryption. This does not affect clusters' ability to decrypt already-encrypted values.

### Per-Cluster Decryption Toggle

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

<Warning>
  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.
</Warning>

### Key Rotation

Periodically rotating encryption keys is a security best practice. Consider rotating keys quarterly or after team member departures.

<Steps>
  <Step title="Go to Encryption Settings">
    Navigate to **Organisation Settings** → **Encryption**.
  </Step>

  <Step title="Click Rotate Keys">
    This generates a new AGE key pair while preserving the old one temporarily.
  </Step>

  <Step title="Re-encrypt Existing Content">
    Before confirming, you must re-encrypt all existing SOPS-encrypted content in your Git repositories with the new public key.
  </Step>

  <Step title="Confirm Rotation">
    Once all content is re-encrypted, click **Confirm Rotation** to finalize. The old private key is then securely deleted.
  </Step>
</Steps>

<Warning>
  **Critical**: You must re-encrypt all content before confirming rotation. Failing to do so will result in decryption failures for content encrypted with the old key.
</Warning>

#### Cancelling Key Rotation

If you need to abort a rotation in progress, click **Cancel Rotation**. The new key pair is discarded and the existing key remains active.

***

## Related

<CardGroup cols={2}>
  <Card title="Stacks" icon="cubes" href="/concepts/stacks">
    Build and deploy reusable Stack configurations.
  </Card>

  <Card title="GitOps" icon="git-alt" href="/concepts/gitops">
    Sync Stacks with Git repositories.
  </Card>

  <Card title="Cloudflare Tunnel" icon="cloud" href="/guides/cloudflare-tunnel">
    Encrypt tunnel tokens with SOPS.
  </Card>

  <Card title="Manifests" icon="file-code" href="/concepts/manifests">
    Create custom Kubernetes resources with encryption.
  </Card>
</CardGroup>
