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

# Using the InfisicalStaticSecret CRD

> Learn how to sync secrets from Infisical and store them as native Kubernetes secret resources using the v1beta1 InfisicalStaticSecret CRD.

The `InfisicalStaticSecret` CRD syncs secrets from Infisical into your Kubernetes cluster as native Kubernetes Secrets or ConfigMaps.
It replaces the v1alpha1 `InfisicalSecret` CRD with a cleaner design: authentication and connection details are defined as separate reusable resources ([InfisicalAuth](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-auth-crd.md) and [InfisicalConnection](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-connection-crd.md)) instead of inline blocks.

### Prerequisites

Before creating an `InfisicalStaticSecret`, you need:

1. An [InfisicalConnection](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-connection-crd.md) resource configured with your Infisical host and TLS settings.
2. An [InfisicalAuth](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-auth-crd.md) resource configured with your machine identity authentication method.

### Examples

Each example below contains every resource you need to apply: the credential `Secret`, `InfisicalConnection`, `InfisicalAuth`, and `InfisicalStaticSecret`.
Copy the one that matches your environment, replace the placeholder values, and `kubectl apply -f` it.

For more details on each resource, see the [InfisicalConnection](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-connection-crd.md) and [InfisicalAuth](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-auth-crd.md) documentation or access our [samples](https://github.com/Infisical/kubernetes-operator/tree/main/examples/v1beta1/infisicalstaticsecret).

> [accordion] Kubernetes Auth (Recommended)
>
> Best for workloads already running in Kubernetes.
>
> This example uses **short-lived service account tokens**. If you prefer to use a Gateway as the token reviewer instead, see the [Kubernetes Auth guide](https://infisical.com/docs/documentation/platform/identities/kubernetes-auth.md) for alternative setup options.
>
> ```yaml complete-kubernetes-auth.yaml theme={"dark"}
> # 1. Service account used by the operator for token review
> apiVersion: v1
> kind: ServiceAccount
> metadata:
>   name: infisical-service-account
>   namespace: default
> ---
> # 2. Bind the service account to system:auth-delegator so it can
> #    validate tokens via the Kubernetes TokenReview API
> apiVersion: rbac.authorization.k8s.io/v1
> kind: ClusterRoleBinding
> metadata:
>   name: infisical-service-account-role-binding
> roleRef:
>   apiGroup: rbac.authorization.k8s.io
>   kind: ClusterRole
>   name: system:auth-delegator
> subjects:
>   - kind: ServiceAccount
>     name: infisical-service-account
>     namespace: default
> ---
> # 3. Kubernetes secret holding the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: kubernetes-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
> ---
> # 4. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 5. Auth using a Kubernetes service account token
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: kubernetes
>   kubernetes:
>     identityIdRef:
>       name: kubernetes-credentials
>       namespace: default
>       key: identityId
>     serviceAccountRef:
>       name: infisical-service-account
>       namespace: default
> ---
> # 6. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] Universal Auth
>
> Platform-agnostic authentication using a client ID and client secret. Works in any environment.
>
> ```yaml complete-universal-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding the client credentials
> apiVersion: v1
> kind: Secret
> metadata:
>   name: universal-auth-credentials
>   namespace: default
> type: Opaque
> stringData:
>   clientId: <your-identity-client-id>
>   clientSecret: <your-identity-client-secret>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using Universal Auth credentials
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: universal
>   universal:
>     clientIdRef:
>       name: universal-auth-credentials
>       namespace: default
>       key: clientId
>     clientSecretRef:
>       name: universal-auth-credentials
>       namespace: default
>       key: clientSecret
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] AWS IAM Auth
>
> For workloads running in AWS (EC2, Lambda, EKS). The operator uses the instance's IAM role to authenticate.
>
> ```yaml complete-aws-iam-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: aws-iam-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using AWS IAM
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: aws-iam
>   awsIam:
>     identityIdRef:
>       name: aws-iam-credentials
>       namespace: default
>       key: identityId
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] Azure Auth
>
> For workloads running in Azure. Uses the Azure managed identity attached to the compute resource.
>
> ```yaml complete-azure-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: azure-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using Azure managed identity
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: azure
>   azure:
>     identityIdRef:
>       name: azure-credentials
>       namespace: default
>       key: identityId
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] GCP ID Token Auth
>
> For workloads running in GCP. Uses the GCP metadata server to obtain an ID token.
>
> ```yaml complete-gcp-id-token-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: gcp-id-token-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using a GCP ID token
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: gcp-id-token
>   gcpIdToken:
>     identityIdRef:
>       name: gcp-id-token-credentials
>       namespace: default
>       key: identityId
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] GCP IAM Auth
>
> Uses a GCP service account key file for authentication. Works both inside and outside GCP.
>
> > [!NOTE]
> >
> > The `serviceAccountKeyFilePath` must point to a file that exists inside the **operator pod**. You will need to create a Kubernetes Secret with your GCP service account key and mount it as a volume in the operator Deployment (or via Helm values). See the [GCP IAM Auth](https://infisical.com/docs/documentation/platform/identities/gcp-auth.md) guide for details.
>
> ```yaml complete-gcp-iam-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: gcp-iam-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using GCP IAM with a service account key
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: gcp-iam
>   gcpIam:
>     identityIdRef:
>       name: gcp-iam-credentials
>       namespace: default
>       key: identityId
>     serviceAccountKeyFilePath: /path/to/service-account-key.json
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

> [accordion] LDAP Auth
>
> Authenticates using LDAP credentials (username and password).
>
> ```yaml complete-ldap-auth.yaml theme={"dark"}
> # 1. Kubernetes secret holding LDAP credentials and the machine identity ID
> apiVersion: v1
> kind: Secret
> metadata:
>   name: ldap-credentials
>   namespace: default
> type: Opaque
> stringData:
>   identityId: <your-machine-identity-id>
>   username: <your-ldap-username>
>   password: <your-ldap-password>
> ---
> # 2. Connection to the Infisical instance
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalConnection
> metadata:
>   name: my-infisical-connection
>   namespace: default
> spec:
>   address: https://app.infisical.com
> ---
> # 3. Auth using LDAP
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalAuth
> metadata:
>   name: my-infisical-auth
>   namespace: default
> spec:
>   infisicalConnectionRef:
>     name: my-infisical-connection
>     namespace: default
>   method: ldap
>   ldap:
>     identityIdRef:
>       name: ldap-credentials
>       namespace: default
>       key: identityId
>     usernameRef:
>       name: ldap-credentials
>       namespace: default
>       key: username
>     passwordRef:
>       name: ldap-credentials
>       namespace: default
>       key: password
> ---
> # 4. Sync secrets into a native Kubernetes Secret
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   namespace: default
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   syncOptions:
>     refreshInterval: 60s
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```

## CRD properties

> [accordion] infisicalAuthRef
>
> A reference to the `InfisicalAuth` resource that defines how to authenticate with Infisical. The connection to the Infisical instance is resolved through the `InfisicalAuth` resource's `infisicalConnectionRef` field.
>
> | Field | Required | Description |
> | - | - | - |
> | `name` | Yes | The name of the `InfisicalAuth` resource. |
> | `namespace` | Yes | The namespace of the `InfisicalAuth` resource. |
>
> See the [InfisicalAuth CRD](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-auth-crd.md) documentation for details.

> [accordion] syncOptions
>
> Controls the synchronization behavior.
>
> | Field | Required | Description |
> | - | - | - |
> | `refreshInterval` | Yes | How often to resync (e.g. `60s`, `5m`, `1h`). |
> | `instantUpdates` | No | When `true`, the operator receives real-time push updates from Infisical. Please note that this is an Enterprise feature. |

> [accordion] sources
>
> An array of secret sources to fetch from Infisical. All secrets from all sources are merged and made available to every target.
>
> When multiple sources produce a secret with the same key, the **first occurrence wins** — sources listed earlier in the array take precedence over later ones. Direct secrets always take precedence over imported secrets, regardless of source order.
>
> To access a secret that was dropped by the merge (e.g. a duplicate key under a different path or source), use the `secretFrom` template function to select it by its full path.
>
> | Field | Required | Description |
> | - | - | - |
> | `projectId` | No | The Infisical project ID. |
> | `projectSlug` | No | The Infisical project slug. |
> | `environmentSlug` | Yes | The environment slug (e.g. `dev`, `staging`, `prod`). |
> | `secretPath` | Yes | The path to the secrets. Root is `/`. |
> | `tagSlugs` | No | Filter secrets by tag slugs. |
> | `recursive` | No | Whether to recursively fetch secrets from sub-folders. Defaults to `false`. |
>
> > [!NOTE]
> >
> > You have to provide either `projectId` or `projectSlug`. If none or both are set, the CRD validation will fail.
>
> ```yaml theme={"dark"}
> sources:
>   - projectId: <your-project-id>
>     environmentSlug: dev
>     secretPath: /app
>   - projectId: <your-project-id>
>     environmentSlug: dev
>     secretPath: /shared
>     recursive: true
>     tagSlugs:
>       - backend
> ```

> [accordion] targets
>
> An array of Kubernetes Secrets or ConfigMaps to create/update with the fetched secrets.
>
> | Field | Required | Description |
> | - | - | - |
> | `name` | Yes | Name of the managed Kubernetes Secret or ConfigMap. |
> | `namespace` | Yes | Namespace of the managed Kubernetes Secret or ConfigMap. |
> | `kind` | Yes | The type of resource to create. Must be `Secret` or `ConfigMap`. |
> | `secretType` | No | Override the default `Opaque` secret type. Only applies when `kind: Secret`. When using a typed secret (e.g. `kubernetes.io/tls`, `kubernetes.io/dockerconfigjson`), you must use a `template` to produce the keys that Kubernetes requires for that type, otherwise the API server will reject the resource. |
> | `creationPolicy` | Yes | `Owner` sets a Kubernetes owner reference so the managed resource is automatically deleted by the garbage collector when the `InfisicalStaticSecret` is removed. `Orphan` leaves the managed resource in place. |
> | `template` | No | Optional templating configuration (see [Templating](#templating) below). |
> | `metadata` | No | Custom `labels` and `annotations` to set on the managed resource. When set, these values are used as-is and **not** merged with the `InfisicalStaticSecret` CRD metadata. |
>
> > [!WARNING]
> >
> > ConfigMaps are intended for non-sensitive data only. Use `kind: Secret` for sensitive values.
>
> ```yaml theme={"dark"}
> targets:
>   - name: app-secrets
>     namespace: default
>     kind: Secret
>     creationPolicy: Owner
>   - name: app-config
>     namespace: default
>     kind: ConfigMap
>     creationPolicy: Orphan
>     metadata:
>       labels:
>         app: my-app
>       annotations:
>         description: "Synced from Infisical"
> ```

## Templating

Fetching secrets from Infisical as-is via the operator may not be enough. This is where templating functionality may be helpful.
Using Go templates, you can format, combine, and create new key-value pairs from secrets fetched from Infisical before storing them as Kubernetes Secrets or ConfigMaps.

When a template is set, only the keys defined in `template.data` are included in the output.
When no template is set, all fetched secrets are included as-is.

Each secret is available in the template context as `.SECRET_KEY`, which is an object with two accessors:

* `.Value`: the secret value.
* `.SecretPath`: the path of the secret in Infisical.

### Key/value template map

```yaml theme={"dark"}
targets:
  - name: my-app-config
    namespace: default
    kind: Secret
    creationPolicy: Owner
    template:
      engineVersion: v1
      data:
        DSN: "{{ .SECRET.Value }}.namespace:{{ .SECRET2.Value }}"
        ANOTHER_ONE: "{{ .SECRET3.Value }}"
```

### Bulk string template with a loop

```yaml theme={"dark"}
targets:
  - name: all-secrets
    namespace: default
    kind: Secret
    creationPolicy: Owner
    template:
      engineVersion: v1
      data: |
        {{- range $key, $secret := . }}
        {{ $key }}: "{{ $secret.Value }}"
        {{- end }}
```

To help transform your secrets further, the operator provides a set of built-in functions that you can use in your templates.

### Available helper functions

The Infisical Secrets Operator exposes a wide range of helper functions to make it easier to work with secrets in Kubernetes.

> [accordion] encodeBase64
>
> Encodes a string to a base64-encoded string (e.g. `hello world` becomes `aGVsbG8gd29ybGQ=`).
>
> **Signature**
>
> ```go theme={"dark"}
> encodeBase64(plainString string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     ENCODED_SECRET: "{{ .MY_SECRET.Value | encodeBase64 }}"
> ```

> [accordion] decodeBase64ToBytes
>
> Decodes a base64-encoded string back to its original value (e.g. `aGVsbG8gd29ybGQ=` becomes `hello world`).
>
> **Signature**
>
> ```go theme={"dark"}
> decodeBase64ToBytes(encodedString string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     DECODED_SECRET: "{{ .MY_BASE64_SECRET.Value | decodeBase64ToBytes }}"
> ```

> [accordion] pkcs12key
>
> Extracts all private keys from a PKCS#12 archive and returns them as PKCS#8 PEM-encoded blocks (`-----BEGIN PRIVATE KEY-----...`).
> The archive must not be password-protected — use `pkcs12keyPass` for password-protected archives.
>
> **Signature**
>
> ```go theme={"dark"}
> pkcs12key(input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     tls.key: "{{ .TLS_CERT_PKCS12.Value | pkcs12key }}"
> ```

> [accordion] pkcs12keyPass
>
> Same as `pkcs12key`, but uses the provided password to decrypt the PKCS#12 archive.
>
> **Signature**
>
> ```go theme={"dark"}
> pkcs12keyPass(password string, input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     tls.key: '{{ pkcs12keyPass "my-password" .TLS_CERT_PKCS12.Value }}'
> ```

> [accordion] pkcs12cert
>
> Extracts all certificates from a PKCS#12 archive and returns them as an ordered PEM chain (`-----BEGIN CERTIFICATE-----...`).
> Sort order: **leaf → intermediate(s) → root**. If disjunct or multiple leaf certs are provided, they are returned as-is.
> The archive must not be password-protected — use `pkcs12certPass` for password-protected archives.
>
> **Signature**
>
> ```go theme={"dark"}
> pkcs12cert(input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     tls.crt: "{{ .TLS_CERT_PKCS12.Value | pkcs12cert }}"
> ```

> [accordion] pkcs12certPass
>
> Same as `pkcs12cert`, but uses the provided password to decrypt the PKCS#12 archive.
>
> **Signature**
>
> ```go theme={"dark"}
> pkcs12certPass(password string, input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     tls.crt: '{{ pkcs12certPass "my-password" .TLS_CERT_PKCS12.Value }}'
> ```

> [accordion] pemToPkcs12
>
> Takes a PEM-encoded certificate and private key and creates a base64-encoded PKCS#12 archive.
> The output is not password-protected — use `pemToPkcs12Pass` to set a password.
>
> **Signature**
>
> ```go theme={"dark"}
> pemToPkcs12(cert string, key string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     keystore.p12: '{{ pemToPkcs12 .TLS_CERT.Value .TLS_KEY.Value }}'
> ```

> [accordion] pemToPkcs12Pass
>
> Same as `pemToPkcs12`, but encrypts the PKCS#12 archive with the provided password.
>
> **Signature**
>
> ```go theme={"dark"}
> pemToPkcs12Pass(cert string, key string, password string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     keystore.p12: '{{ pemToPkcs12Pass .TLS_CERT.Value .TLS_KEY.Value "my-password" }}'
> ```

> [accordion] fullPemToPkcs12
>
> Takes a full PEM-encoded certificate chain (leaf + intermediates + root) and a private key, and creates a base64-encoded PKCS#12 archive that includes the entire chain.
> The output is not password-protected — use `fullPemToPkcs12Pass` to set a password.
>
> **Signature**
>
> ```go theme={"dark"}
> fullPemToPkcs12(cert string, key string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     keystore.p12: '{{ fullPemToPkcs12 .TLS_FULL_CHAIN.Value .TLS_KEY.Value }}'
> ```

> [accordion] fullPemToPkcs12Pass
>
> Same as `fullPemToPkcs12`, but encrypts the PKCS#12 archive with the provided password.
>
> **Signature**
>
> ```go theme={"dark"}
> fullPemToPkcs12Pass(cert string, key string, password string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     keystore.p12: '{{ fullPemToPkcs12Pass .TLS_FULL_CHAIN.Value .TLS_KEY.Value "my-password" }}'
> ```

> [accordion] filterPEM
>
> Filters PEM blocks by type from a bundle containing multiple PEM blocks (e.g. extract only `CERTIFICATE` or `PRIVATE KEY` blocks).
> Common PEM types: `CERTIFICATE`, `PRIVATE KEY`, `PUBLIC KEY`, `RSA PRIVATE KEY`.
>
> **Signature**
>
> ```go theme={"dark"}
> filterPEM(pemType string, input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     ca.crt: '{{ filterPEM "CERTIFICATE" .TLS_BUNDLE.Value }}'
>     tls.key: '{{ filterPEM "PRIVATE KEY" .TLS_BUNDLE.Value }}'
> ```

> [accordion] filterCertChain
>
> Filters PEM certificates by their position in a certificate chain. The chain is automatically ordered before filtering.
> Accepted types: `leaf` (end-entity certificate), `intermediate` (all intermediate CA certificates), `root` (root CA certificate).
> Returns an empty string if the requested type is not present in the chain.
>
> **Signature**
>
> ```go theme={"dark"}
> filterCertChain(certType string, input string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     tls.crt: '{{ filterCertChain "leaf" .TLS_CHAIN.Value }}'
>     ca.crt: '{{ filterCertChain "root" .TLS_CHAIN.Value }}'
>     intermediate.crt: '{{ filterCertChain "intermediate" .TLS_CHAIN.Value }}'
> ```

> [accordion] jwkPublicKeyPem
>
> Takes a JSON-serialized JWK and returns a PEM block of type `PUBLIC KEY` containing the public key.
> Uses [`x509.MarshalPKIXPublicKey`](https://pkg.go.dev/crypto/x509#MarshalPKIXPublicKey) internally.
>
> **Signature**
>
> ```go theme={"dark"}
> jwkPublicKeyPem(jwkJson string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     public.pem: "{{ .MY_JWK.Value | jwkPublicKeyPem }}"
> ```

> [accordion] jwkPrivateKeyPem
>
> Takes a JSON-serialized JWK and returns a PEM block of type `PRIVATE KEY` containing the private key.
> Uses [`x509.MarshalPKCS8PrivateKey`](https://pkg.go.dev/crypto/x509#MarshalPKCS8PrivateKey) internally.
>
> **Signature**
>
> ```go theme={"dark"}
> jwkPrivateKeyPem(jwkJson string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     private.pem: "{{ .MY_JWK.Value | jwkPrivateKeyPem }}"
> ```

> [accordion] toYaml
>
> Marshals a value to a YAML string. Returns an empty string on marshal error.
>
> **Signature**
>
> ```go theme={"dark"}
> toYaml(v any) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     config.yaml: "{{ .APP_CONFIG.Value | fromYaml | toYaml }}"
> ```

> [accordion] fromYaml
>
> Parses a YAML string into a `map[string]any`, useful for extracting individual fields from a YAML-formatted secret (e.g. `(fromYaml .DB_CONFIG.Value).host` returns the `host` field).
>
> **Signature**
>
> ```go theme={"dark"}
> fromYaml(str string) map[string]any
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     DB_HOST: '{{ (fromYaml .DB_CONFIG.Value).host }}'
>     DB_PORT: '{{ (fromYaml .DB_CONFIG.Value).port }}'
> ```

> [accordion] secretFrom
>
> > [!NOTE]
> >
> > This function is only available in `v1beta1` resources (e.g. `InfisicalStaticSecret`).
>
> Resolves a secret from a specific folder path within the Infisical project. Takes a path and a secret name as parameters, and returns the secret's value by default. You can optionally use `.Value` or `.SecretPath` accessors on the result -- if omitted, `.Value` is used.
>
> This is especially useful when multiple secrets share the same key, either from recursive fetches across different paths or from multiple sources. In both cases, the merge strategy only keeps the first occurrence (read more [here](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-static-secret-crd.md#sources)), so `secretFrom` lets you explicitly select the one you need by its full path.
>
> > [!NOTE]
> >
> > If multiple sources contain a secret with the same name and path, the secret from the first source listed in the `sources` array will be used.
>
> **Signature**
>
> ```go theme={"dark"}
> secretFrom(path string, secretName string) string
> ```
>
> **Template usage**
>
> ```yaml theme={"dark"}
> template:
>   data:
>     # .Value is implicit when no accessor is specified
>     DB_PASSWORD: '{{ secretFrom "/databases/postgres" "DB_PASSWORD" }}'
>     # Explicit .Value accessor (same result as above)
>     STRIPE_API_KEY: '{{ (secretFrom "/services/payments" "API_KEY").Value }}'
>     # Using .SecretPath to get the folder path instead of the value
>     DATADOG_API_KEY: '{{ (secretFrom "/services/observability" "API_KEY").Value }}'
>     DATADOG_API_KEY_PATH: '{{ (secretFrom "/services/observability" "API_KEY").SecretPath }}'
> ```

### Sprig functions

The Infisical Secrets Operator integrates with the [Sprig library](https://github.com/Masterminds/sprig) to provide additional helper functions.

> [!NOTE]
>
> We've removed `expandEnv` and `env` from the supported functions for security reasons.

## Metadata propagation

The operator provides flexible options for managing labels and annotations on managed Kubernetes Secrets and ConfigMaps.

> [accordion] Default behavior
>
> By default, the operator merges labels and annotations from the `InfisicalStaticSecret` resource into each managed Secret or ConfigMap that does not define target-level `metadata`.
>
> Conflict behavior:
>
> * Existing labels and annotations that were not previously managed by the operator are preserved.
> * Labels and annotations from the `InfisicalStaticSecret` resource take precedence when the same key already exists on the managed resource.
> * Labels and annotations previously propagated by the operator are removed if they are removed from the `InfisicalStaticSecret` resource.
> * Kubernetes system annotations and Infisical Operator tracking annotations are preserved.
>
> ```yaml theme={"dark"}
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   labels:
>     label-to-be-passed-to-managed-secret: sample-value
>   annotations:
>     example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: dev
>       secretPath: /
>   targets:
>     - name: managed-secret
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
> ```
>
> This creates a managed Secret with the inherited metadata:
>
> ```yaml theme={"dark"}
> apiVersion: v1
> kind: Secret
> metadata:
>   name: managed-secret
>   namespace: default
>   labels:
>     label-to-be-passed-to-managed-secret: sample-value
>   annotations:
>     example.com/annotation-to-be-passed-to-managed-secret: sample-value
>     secrets.infisical.com/version: W/"3f1-ZyOSsrCLGSkAhhCkY2USPu2ivRw"
> type: Opaque
> data: ...
> ```

> [accordion] Custom target metadata
>
> When you specify `metadata` on a target, those labels and annotations are used exclusively for that managed resource:
>
> * Target `metadata.labels` replaces the managed resource's labels.
>
> * Target `metadata.annotations` replaces user-defined annotations on the managed resource.
>
> * Kubernetes system annotations and the Infisical version annotation are preserved.
>
> * Labels and annotations from the `InfisicalStaticSecret` resource are not propagated to that target.
>
> * This lets you keep CRD-specific metadata separate from managed resource metadata.
>
> > [!TIP]
> >
> > To prevent any propagation while using target `metadata`, pass empty objects for labels and/or annotations:
> >
> > ```yaml theme={"dark"}
> > targets:
> >   - name: managed-secret
> >     namespace: default
> >     kind: Secret
> >     creationPolicy: Owner
> >     metadata:
> >       labels: {}
> >       annotations: {}
> > ```
>
> ```yaml theme={"dark"}
> apiVersion: secrets.infisical.com/v1beta1
> kind: InfisicalStaticSecret
> metadata:
>   name: my-static-secret
>   labels:
>     managed-by: infisical-operator
>   annotations:
>     example.com/cr-specific: "metadata"
> spec:
>   infisicalAuthRef:
>     name: my-infisical-auth
>     namespace: default
>   sources:
>     - projectId: <your-project-id>
>       environmentSlug: prod
>       secretPath: /
>   targets:
>     - name: managed-secret-with-custom-metadata
>       namespace: default
>       kind: Secret
>       creationPolicy: Owner
>       metadata:
>         labels:
>           app: my-application
>           environment: production
>           tier: backend
>         annotations:
>           secret.example.com/description: "Production database credentials"
>           secret.example.com/owner: "platform-team"
> ```
>
> This creates a managed Secret with only the target metadata:
>
> ```yaml theme={"dark"}
> apiVersion: v1
> kind: Secret
> metadata:
>   name: managed-secret-with-custom-metadata
>   namespace: default
>   labels:
>     app: my-application
>     environment: production
>     tier: backend
>   annotations:
>     secret.example.com/description: "Production database credentials"
>     secret.example.com/owner: "platform-team"
> type: Opaque
> data: ...
> ```

## Applying the CRD

```bash theme={"dark"}
kubectl apply -f infisical-static-secret.yaml
```

Verify the managed secret was created:

```bash theme={"dark"}
kubectl get secrets -n <namespace>
```

## Using the managed resource in your deployment

The managed Kubernetes Secret or ConfigMap works like any other native resource. You can reference it via `envFrom`, `env`, or `volumes`:

> [tab] Secret
>
> > [accordion] envFrom
> >
> > ```yaml theme={"dark"}
> > envFrom:
> >   - secretRef:
> >       name: managed-secret
> > ```
>
> > [accordion] env
> >
> > ```yaml theme={"dark"}
> > env:
> >   - name: SECRET_NAME
> >     valueFrom:
> >       secretKeyRef:
> >         name: managed-secret
> >         key: SOME_SECRET_KEY
> > ```
>
> > [accordion] volumes
> >
> > ```yaml theme={"dark"}
> > volumes:
> >   - name: secrets-volume
> >     secret:
> >       secretName: managed-secret
> > ```

> [tab] ConfigMap
>
> > [accordion] envFrom
> >
> > ```yaml theme={"dark"}
> > envFrom:
> >   - configMapRef:
> >       name: managed-configmap
> > ```
>
> > [accordion] env
> >
> > ```yaml theme={"dark"}
> > env:
> >   - name: CONFIG_VALUE
> >     valueFrom:
> >       configMapKeyRef:
> >         name: managed-configmap
> >         key: SOME_CONFIG_KEY
> > ```
>
> > [accordion] volumes
> >
> > ```yaml theme={"dark"}
> > volumes:
> >   - name: config-volume
> >     configMap:
> >       name: managed-configmap
> > ```

## Automatic redeployment

Pods that consume a managed Secret or ConfigMap don't reload automatically when the underlying data changes. Without a restart, the workload may continue using stale values, especially when secrets are injected as environment variables.

To trigger a rolling restart when the managed resource updates, add the following annotation to the Deployment, StatefulSet, or DaemonSet that consumes it:

```yaml theme={"dark"}
secrets.infisical.com/auto-reload: "true"
```

> [accordion] Deployment example
>
> > [tab] Secret
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: Deployment
> > metadata:
> >   name: nginx-deployment
> >   labels:
> >     app: nginx
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   replicas: 1
> >   selector:
> >     matchLabels:
> >       app: nginx
> >   template:
> >     metadata:
> >       labels:
> >         app: nginx
> >     spec:
> >       containers:
> >         - name: nginx
> >           image: nginx:1.14.2
> >           envFrom:
> >             - secretRef:
> >                 name: managed-secret
> >           ports:
> >             - containerPort: 80
> > ```
>
> > [tab] ConfigMap
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: Deployment
> > metadata:
> >   name: app-deployment
> >   labels:
> >     app: app
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   replicas: 1
> >   selector:
> >     matchLabels:
> >       app: app
> >   template:
> >     metadata:
> >       labels:
> >         app: app
> >     spec:
> >       containers:
> >         - name: app
> >           image: mycompany/app:latest
> >           envFrom:
> >             - configMapRef:
> >                 name: managed-configmap
> >           ports:
> >             - containerPort: 80
> > ```

> [accordion] DaemonSet example
>
> > [tab] Secret
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: DaemonSet
> > metadata:
> >   name: log-agent
> >   labels:
> >     app: log-agent
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   selector:
> >     matchLabels:
> >       app: log-agent
> >   template:
> >     metadata:
> >       labels:
> >         app: log-agent
> >     spec:
> >       containers:
> >         - name: log-agent
> >           image: mycompany/log-agent:latest
> >           envFrom:
> >             - secretRef:
> >                 name: managed-secret
> >           volumeMounts:
> >             - name: secrets-volume
> >               mountPath: /etc/secrets
> >               readOnly: true
> >       volumes:
> >         - name: secrets-volume
> >           secret:
> >             secretName: managed-secret
> > ```
>
> > [tab] ConfigMap
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: DaemonSet
> > metadata:
> >   name: log-agent
> >   labels:
> >     app: log-agent
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   selector:
> >     matchLabels:
> >       app: log-agent
> >   template:
> >     metadata:
> >       labels:
> >         app: log-agent
> >     spec:
> >       containers:
> >         - name: log-agent
> >           image: mycompany/log-agent:latest
> >           envFrom:
> >             - configMapRef:
> >                 name: managed-configmap
> >           volumeMounts:
> >             - name: config-volume
> >               mountPath: /etc/config
> >               readOnly: true
> >       volumes:
> >         - name: config-volume
> >           configMap:
> >             name: managed-configmap
> > ```

> [accordion] StatefulSet example
>
> > [tab] Secret
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: StatefulSet
> > metadata:
> >   name: db-worker
> >   labels:
> >     app: db-worker
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   selector:
> >     matchLabels:
> >       app: db-worker
> >   serviceName: "db-worker"
> >   replicas: 2
> >   template:
> >     metadata:
> >       labels:
> >         app: db-worker
> >     spec:
> >       containers:
> >         - name: db-worker
> >           image: mycompany/db-worker:stable
> >           env:
> >             - name: DATABASE_PASSWORD
> >               valueFrom:
> >                 secretKeyRef:
> >                   name: managed-secret
> >                   key: DB_PASSWORD
> >           ports:
> >             - containerPort: 5432
> > ```
>
> > [tab] ConfigMap
> >
> > ```yaml theme={"dark"}
> > apiVersion: apps/v1
> > kind: StatefulSet
> > metadata:
> >   name: db-worker
> >   labels:
> >     app: db-worker
> >   annotations:
> >     secrets.infisical.com/auto-reload: "true"
> > spec:
> >   selector:
> >     matchLabels:
> >       app: db-worker
> >   serviceName: "db-worker"
> >   replicas: 2
> >   template:
> >     metadata:
> >       labels:
> >         app: db-worker
> >     spec:
> >       containers:
> >         - name: db-worker
> >           image: mycompany/db-worker:stable
> >           env:
> >             - name: DATABASE_HOST
> >               valueFrom:
> >                 configMapKeyRef:
> >                   name: managed-configmap
> >                   key: DATABASE_HOST
> >           ports:
> >             - containerPort: 5432
> > ```

> [!NOTE]
>
> When a managed Secret or ConfigMap is updated, the operator checks for any Deployments, DaemonSets, or StatefulSets that consume the updated resource and have the `secrets.infisical.com/auto-reload: "true"` annotation. For each matching workload, the operator triggers a rolling restart so the pods pick up the latest values.

## Metrics

The operator exposes Prometheus metrics about each `InfisicalStaticSecret`, which you can use to count your resources, alert when a sync fails, and track how workloads use automatic redeployment. To collect the metrics, [enable the ServiceMonitor](https://infisical.com/docs/integrations/platforms/kubernetes/overview#metrics-and-prometheus). Each metric has `namespace` and `name` labels that identify the `InfisicalStaticSecret`.

> [!NOTE]
>
> These metrics require operator v0.11.14 or later.

> [accordion] infisical\_static\_secrets
>
> One series per resource with the value `1` and the following labels:
>
> * `status`: the result of the resource's last sync (`synced`, `failed`, or `unknown` before the first sync)
> * `instant_updates`: `true` if `syncOptions.instantUpdates` is enabled, otherwise `false`
>
> ```ruby PromQL theme={"dark"}
> # InfisicalStaticSecret resources
> count(infisical_static_secrets)
>
> # InfisicalStaticSecret resources whose last sync failed (use as an alert expression)
> infisical_static_secrets{status="failed"}
>
> # InfisicalStaticSecret resources with instant updates enabled
> count(infisical_static_secrets{instant_updates="true"})
> ```

> [accordion] infisical\_static\_secret\_refresh\_interval\_seconds
>
> The resource's `syncOptions.refreshInterval`, in seconds.
>
> ```ruby PromQL theme={"dark"}
> # Average refresh interval (seconds)
> avg(infisical_static_secret_refresh_interval_seconds)
> ```

> [accordion] infisical\_static\_secret\_auto\_reload\_workloads
>
> The number of workloads with [automatic redeployment](#automatic-redeployment) enabled that consume the resource's managed Secret or ConfigMap.
>
> ```ruby PromQL theme={"dark"}
> # InfisicalStaticSecret resources with automatic redeployment enabled on at least one workload
> count(infisical_static_secret_auto_reload_workloads > 0)
> ```

> [accordion] infisical\_static\_secret\_workload\_redeploys\_total
>
> The number of workloads that automatic redeployment has restarted.
>
> ```ruby PromQL theme={"dark"}
> # Workloads restarted by automatic redeployment in the last 24 hours
> sum(increase(infisical_static_secret_workload_redeploys_total[24h]))
> ```

## Migrating from InfisicalSecret

If you sync secrets with the v1alpha1 [InfisicalSecret CRD](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-secret-crd.md), an `InfisicalStaticSecret` can take over the Secrets and ConfigMaps that the `InfisicalSecret` manages. The operator doesn't delete or recreate the Secrets and ConfigMaps during the migration.

The `InfisicalStaticSecret` spec is different from the `InfisicalSecret` spec:

* It authenticates through an [InfisicalAuth](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-auth-crd.md) resource, which connects to Infisical through an [InfisicalConnection](https://infisical.com/docs/integrations/platforms/kubernetes/infisical-connection-crd.md) resource
* Its templates use the [v1 template syntax](#templating), which has no `includeAllSecrets` option

**Step 1**

Upgrade the operator to v0.11.12 or later. Earlier versions can't take over Secrets and ConfigMaps from an `InfisicalSecret`.

**Step 2**

Create an `InfisicalStaticSecret` in the same namespace as the `InfisicalSecret`. Add a target for each Secret and ConfigMap that the `InfisicalSecret` manages, with the same name, namespace, kind, and `creationPolicy`.

> [!NOTE]
>
> If a workload has the [auto-reload annotation](#automatic-redeployment), the workload can restart one time during the migration.

**Step 3**

Check that the `SYNCED` column shows `True` for the `InfisicalStaticSecret`:

```bash theme={"dark"}
kubectl get infisicalstaticsecret <static-secret-name> -n <namespace>
```

> [!WARNING]
>
> Don't go to the next step before `SYNCED` shows `True`. If you delete the `InfisicalSecret` too early, Kubernetes can delete the Secrets and ConfigMaps, and the operator creates them again only at the next `refreshInterval`.

**Step 4**

Delete the `InfisicalSecret`:

```bash theme={"dark"}
kubectl delete infisicalsecret <infisical-secret-name> -n <namespace>
```

The Secrets and ConfigMaps stay in place, and the `InfisicalStaticSecret` manages them from now on.

### How the migration works

If an `InfisicalStaticSecret` target has the same name, namespace, and kind as a Secret or ConfigMap that an `InfisicalSecret` owns, the operator removes the `InfisicalSecret` [owner reference](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/) at the next sync. After that, deleting the `InfisicalSecret` doesn't delete the Secret or ConfigMap. What happens next depends on the target's `creationPolicy`:

* `Owner`: the `InfisicalStaticSecret` becomes the new owner of the Secret or ConfigMap
* `Orphan`: the Secret or ConfigMap stays unowned

An `InfisicalStaticSecret` can only own a Secret or ConfigMap in its own namespace. If the operator can't make the `InfisicalStaticSecret` the owner, the Secret or ConfigMap stays unowned, and the operator still syncs the data to it.

The operator changes the owner and writes the data in the same update, so when `SYNCED` shows `True`, the migration is complete.

## Troubleshooting

You can check the status of your `InfisicalStaticSecret` resource by inspecting its conditions:

```bash theme={"dark"}
kubectl get infisicalstaticsecret my-static-secret -o jsonpath='{.status.conditions}' | jq
```

When reconciliation is successful, the `secrets.infisical.com/LastReconcileStatus` condition will have `Status: "True"`, `Reason: "OK"`, and `Message: "Reconciliation successful"`.

If reconciliation fails, `Status` will be `"False"`, `Reason` will be set to `Error`, and `Message` will contain details about what went wrong.

The `ObservedGeneration` field indicates which generation of the resource spec the operator has last processed. If `ObservedGeneration` is less than `metadata.generation`, the operator hasn't yet reconciled the latest changes to the resource.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.