docs/documentation/guides/kubernetes-operator.mdx
In this tutorial, you will:
InfisicalConnection and InfisicalAuth CRDs.InfisicalStaticSecret CRD and consume it from a demo app.InfisicalPushSecret CRD.InfisicalDynamicSecret CRD.Pushing and dynamic secrets have no v1beta1 equivalents, so those sections use the v1alpha1 InfisicalPushSecret and InfisicalDynamicSecret CRDs, which each carry their own inline authentication block. See the Kubernetes Operator overview for the other CRDs and authentication methods.
</Note>
Before you begin, make sure you have:
kubectl is pointed at, with an API server reachable from your Infisical instance. Confirm with kubectl cluster-info.If you use Infisical Cloud, a local kind, minikube, or Docker Desktop cluster is not reachable and will not work as-is. Use a cluster with a reachable API endpoint, or self-host Infisical somewhere that can reach the cluster. The same applies to the PostgreSQL database for the dynamic-secret step: it must be reachable from your Infisical instance, not just from your cluster, so use a database with a public or peered address. </Warning>
<Info> [Dynamic Secrets](/documentation/platform/dynamic-secrets/overview) are available under the **Pro Tier** and **Enterprise Tier** on Infisical Cloud.If you're self-hosting Infisical, contact [email protected] to purchase an enterprise license. </Info>
```bash
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
helm repo update
helm install infisical-operator infisical-helm-charts/secrets-operator
```
The Helm chart installs every operator CRD, including the `v1beta1` `InfisicalConnection`, `InfisicalAuth`, and `InfisicalStaticSecret` kinds this tutorial uses for the pull path.
Confirm the operator pod is running:
```bash
kubectl get pods -n default
```
You should see a pod whose name begins with `infisical-opera-controller-manager-` in the `Running` state with `1/1` ready:
```text
NAME READY STATUS RESTARTS AGE
infisical-opera-controller-manager-xxxxxxxxxx-xxxxx 1/1 Running 0 30s
```
<Note>
The `infisical-opera` prefix comes from the Helm release and chart names truncated to 15 characters, so a different release name changes it. `helm install` also uses your current kubeconfig namespace: if the pod is not under `-n default`, run `kubectl get pods -A | grep infisical` to find it, and adjust the `-n` flag in later commands to match.
</Note>
1. Log in to [Infisical](https://app.infisical.com/).
2. Select **Access Control** from the left navigation pane.
3. Open the **Machine Identities** tab and create an identity. Give it a name and assign it the **Member** organization role. You grant it project-level access in the next step.
4. Copy the **Identity ID** and record it. It lands in two different places later:
- **Pull path**: you store it in a Kubernetes Secret named `kubernetes-credentials`. The `InfisicalAuth` CRD reads it from there through `identityIdRef`. It is never pasted inline for pull.
- **Push and dynamic**: you paste it inline as `identityId` in each CRD's `authentication.kubernetesAuth` block.
5. Select the identity, then add a **Kubernetes Auth** method with these values:
- **Allowed Service Account Names**: `infisical-service-account`
- **Allowed Namespaces**: `default`
- **Kubernetes Host URL**: the control-plane URL from `kubectl cluster-info`. It is the line beginning "Kubernetes control plane is running at". This URL must be reachable from your Infisical instance, as covered in the prerequisites.
- **Token Reviewer JWT** and **CA Certificate**: leave both blank for now. You generate these in the next steps and return to paste them in. The **CA Certificate** field is on the **Advanced** tab.
<Note>
You will return to this Kubernetes Auth method in the "Wire up the Kubernetes Auth method" step. With this tutorial's RBAC setup, authentication fails until you paste the Token Reviewer JWT. The CA certificate is optional but recommended.
</Note>
<Note>
Admin is used here only to keep the tutorial simple. In production, scope the identity to the least privilege it needs.
</Note>
- `infisical-token-reviewer`: its JWT lets the **Infisical backend** call the Kubernetes TokenReview API to validate tokens presented by the operator. It serves both the pull path and the push/dynamic path.
- `infisical-service-account`: the identity the operator presents when it authenticates to Infisical.
Create the token-reviewer service account:
```bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
name: infisical-token-reviewer
namespace: default
EOF
```
Create a long-lived token secret for it:
```bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: infisical-token-reviewer-token
namespace: default
annotations:
kubernetes.io/service-account.name: infisical-token-reviewer
type: kubernetes.io/service-account-token
EOF
```
Bind the token-reviewer service account to the built-in `system:auth-delegator` ClusterRole. This lets the Infisical backend, acting with this service account's JWT, submit TokenReview requests. Without this binding, TokenReview calls are denied.
```bash
kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: infisical-token-reviewer-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: infisical-token-reviewer
namespace: default
EOF
```
Create the operator's working service account and its token secret:
```bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
name: infisical-service-account
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: infisical-service-account-token
namespace: default
annotations:
kubernetes.io/service-account.name: infisical-service-account
type: kubernetes.io/service-account-token
EOF
```
Associate the token secret with the service account:
```bash
kubectl patch serviceaccount infisical-service-account \
-p '{"secrets": [{"name": "infisical-service-account-token"}]}' -n default
```
You should see `serviceaccount/infisical-service-account patched`.
<Note>
The `infisical-service-account-token` Secret and the `kubectl patch` serve only the `v1alpha1` push and dynamic CRDs, which read the service account's stored token. The `v1beta1` pull path does not use them: `InfisicalAuth` mints a short-lived token (10 minutes) through the Kubernetes TokenRequest API on every login.
</Note>
<Note>
The service-account names and namespaces above must match the **Allowed Service Account Names** (`infisical-service-account`) and **Allowed Namespaces** (`default`) you set on the Kubernetes Auth method. They must also match the `serviceAccountRef` in the `InfisicalAuth` CRD and in the push and dynamic CRDs' inline `kubernetesAuth` blocks. Do not rename them.
</Note>
```bash
kubectl get serviceaccount -n default | grep infisical
```
You should see `infisical-token-reviewer` and `infisical-service-account` (alongside the operator's own `infisical-opera-controller-manager` service account).
Confirm the two token secrets exist:
```bash
kubectl get secrets -n default | grep infisical
```
You should see `infisical-token-reviewer-token` and `infisical-service-account-token`, each of type `kubernetes.io/service-account-token`. The operator's own Helm release secret (`sh.helm.release.v1.infisical-operator...`) also matches the grep; ignore it.
Print the Token Reviewer JWT:
```bash
kubectl get secret infisical-token-reviewer-token -n default \
-o jsonpath='{.data.token}' | base64 -d
```
Print the cluster CA certificate:
```bash
kubectl get secret infisical-token-reviewer-token -n default \
-o jsonpath='{.data.ca\.crt}' | base64 -d
```
Then, in Infisical:
1. Open the Machine Identity and open its **Kubernetes Auth** method for editing.
2. Paste the decoded JWT into the **Token Reviewer JWT** field.
3. Switch to the **Advanced** tab and paste the decoded certificate into the **CA Certificate** field.
4. Save the configuration.
<Info>
The **CA Certificate** field is optional and pairs with the **Verify TLS Certificate** toggle on the **Advanced** tab. When that toggle is on, Infisical validates the Kubernetes API server's TLS certificate against this CA. You can save with the CA left empty, but for production keep verification on and provide the cluster CA.
</Info>
After saving, the Machine Identity can authenticate operator requests.
The v1beta1 pull path splits connection and authentication into two reusable objects. The InfisicalConnection records where your Infisical instance is. The InfisicalAuth records how the operator authenticates to it. Any number of InfisicalStaticSecret resources can then share the same pair.
```yaml
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalConnection
metadata:
name: infisical-connection
namespace: default
spec:
address: https://app.infisical.com
```
<Note>
If you self-host Infisical, set `spec.address` to your instance's base URL instead, for example `https://infisical.example.com`. Provide the base URL only, without a trailing `/api`. The `InfisicalStaticSecret` pull path uses `spec.address` exactly as written and appends the API path itself, so an address that ends in `/api` produces failing requests to `/api/api/...`. For the same reason, always set `spec.address` explicitly. The operator's `INFISICAL_HOST_API` fallback, which the Helm chart sets to `https://app.infisical.com/api`, ends in `/api` and does not work for the pull path.
</Note>
Apply the resource:
```bash
kubectl apply -f infisical-connection.yaml
```
On each reconcile, the operator health-checks the instance by calling `<address>/api/status` and sets the `secrets.infisical.com/IsReady` condition on the result. Healthy connections are re-checked every 5 minutes. Confirm the connection is ready:
```bash
kubectl get infisicalconnection infisical-connection -n default
```
You should see `True` in the `READY` column:
```text
NAME AGE ADDRESS READY
infisical-connection 10s https://app.infisical.com True
```
If `READY` is not `True`, see the Troubleshooting section at the end of this tutorial.
```bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: kubernetes-credentials
namespace: default
type: Opaque
stringData:
identityId: your-identity-id
EOF
```
Confirm the Secret exists and holds the `identityId` key:
```bash
kubectl get secret kubernetes-credentials -n default -o jsonpath='{.data}' | jq 'keys'
```
You should see `["identityId"]`.
```yaml
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalAuth
metadata:
name: infisical-auth
namespace: default
spec:
infisicalConnectionRef:
name: infisical-connection
namespace: default
method: kubernetes
kubernetes:
identityIdRef:
name: kubernetes-credentials
namespace: default
key: identityId
serviceAccountRef:
name: infisical-service-account
namespace: default
```
Field notes:
- `identityIdRef` names the Secret (`name`, `namespace`) and the data key (`key`) holding the Identity ID. There is no inline `identityId` field in `v1beta1`.
- `serviceAccountRef` must name the `infisical-service-account` you created. The operator mints a fresh 10-minute token for it via the TokenRequest API on every login.
- `method` selects the authentication method; this tutorial uses `kubernetes`. See the [`InfisicalAuth` reference](/integrations/platforms/kubernetes/infisical-auth-crd) for the other methods.
Apply the resource:
```bash
kubectl apply -f infisical-auth.yaml
```
On reconcile, the operator performs a real login against the connection. The referenced `InfisicalConnection` must be ready first. Confirm the auth is ready:
```bash
kubectl get infisicalauth infisical-auth -n default
```
You should see `True` in the `READY` column:
```text
NAME CONNECTION METHOD AGE READY
infisical-auth infisical-connection kubernetes 10s True
```
For detail, `kubectl describe infisicalauth infisical-auth -n default` shows the `secrets.infisical.com/IsReady` and `secrets.infisical.com/AuthMethod` conditions. If the login fails, this is where the error message lands; see Troubleshooting.
The InfisicalStaticSecret CRD tells the operator to fetch secrets from Infisical (sources) and write them into native Kubernetes objects (targets). It authenticates through the InfisicalAuth you just created.
```yaml
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalStaticSecret
metadata:
name: infisical-static-secret-demo
namespace: default
spec:
infisicalAuthRef:
name: infisical-auth
namespace: default
syncOptions:
refreshInterval: 1m
instantUpdates: false
sources:
- projectSlug: your-project-slug
environmentSlug: prod
secretPath: "/"
targets:
- name: managed-secret
namespace: default
kind: Secret
creationPolicy: Owner
```
Each recorded value now lands here:
| Recorded value | Where it's used |
|---|---|
| Identity ID | `identityId` key in the `kubernetes-credentials` Secret (via `InfisicalAuth`'s `identityIdRef`) |
| Project slug | `spec.sources[].projectSlug` |
| Environment slug | `spec.sources[].environmentSlug` |
Field notes:
- Each source sets exactly one of `projectSlug` or `projectId`, plus a required `environmentSlug` and `secretPath`. Setting both or neither project field makes the reconcile fail; the error surfaces in the resource's status conditions.
- `syncOptions.refreshInterval` is required. It takes the form `<number><unit>` with units `s`, `m`, `h`, `d`, or `w`, and must be at least 5 seconds. Set `instantUpdates: true` to also receive near-real-time updates over server-sent events; availability is plan-gated server-side.
- Each target requires `name`, `namespace`, `kind` (`Secret` or `ConfigMap`), and `creationPolicy`. `Owner` marks the managed object as owned by the CRD, so it is deleted with it; `Orphan` leaves the object behind. Targets can also apply templates to transform the data.
Apply the resource:
```bash
kubectl apply -f infisical-static-secret.yaml
```
```bash
kubectl get infisicalstaticsecret infisical-static-secret-demo -n default
```
You should see `True` in the `SYNCED` column:
```text
NAME AGE AUTH METHOD SYNCED AFFECTED DEPLOYMENTS
infisical-static-secret-demo 15s kubernetes True 0 deployments were redeployed due to secret changes
```
Confirm the operator created the managed secret:
```bash
kubectl get secret managed-secret -n default
```
View the secret contents:
```bash
kubectl get secret managed-secret -n default -o jsonpath='{.data}' | jq
```
You should see a base64-encoded entry for `SMTP_HOST`.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
envFrom:
- secretRef:
name: managed-secret
```
Apply it:
```bash
kubectl apply -f demo-deployment.yaml
```
Wait 15 to 20 seconds, then confirm the deployment and pod are ready:
```bash
kubectl get deployments -n default
kubectl get pods -l app=nginx -n default
```
```bash
kubectl exec -it $(kubectl get pod -l app=nginx -n default -o jsonpath='{.items[0].metadata.name}') -n default -- env | grep SMTP
```
You should see `[email protected]`. The pull sync from Infisical to Kubernetes now works end to end.
The InfisicalPushSecret CRD does the reverse: it reads a Kubernetes Secret and writes its values into Infisical.
This v1alpha1 CRD does not use the InfisicalConnection or InfisicalAuth objects; it carries its own inline authentication block with the Identity ID pasted as a plain identityId string.
```yaml
apiVersion: v1
kind: Secret
metadata:
name: push-secret-demo
namespace: default
type: Opaque
stringData:
API_KEY: example-value
```
Apply and verify it:
```bash
kubectl apply -f source-secret.yaml
kubectl get secret push-secret-demo -n default -o yaml
```
```yaml
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalPushSecret
metadata:
name: push-secret-demo
namespace: default
spec:
updatePolicy: Replace
deletionPolicy: Delete
destination:
projectSlug: your-project-slug
environmentSlug: prod
secretsPath: "/"
authentication:
kubernetesAuth:
identityId: your-identity-id
serviceAccountRef:
name: infisical-service-account
namespace: default
push:
secret:
secretName: push-secret-demo
secretNamespace: default
```
Note the differences from the pull flow you built with `InfisicalStaticSecret`:
- **Authentication is inline.** The CRD carries its own `authentication.kubernetesAuth` block, with the Identity ID as a plain `identityId` string and the same `serviceAccountRef`. The shared connection and auth objects are not in play here.
- Scope lives under `spec.destination` (`projectSlug`, `environmentSlug`, `secretsPath`) instead of `spec.sources`, and the environment field is `environmentSlug`.
- The direction reverses: `push.secret` names the source Kubernetes Secret to read from, and there are no `targets`.
- `updatePolicy: Replace` makes the operator overwrite values that already exist in Infisical. Without it, pre-existing Infisical secrets are left untouched.
- `deletionPolicy: Delete` makes the operator remove the pushed secrets from Infisical when you delete this CRD. This matters during cleanup.
- `push.secret.secretName` must match the source secret you created (`push-secret-demo`).
<Note>
In `spec.destination`, set exactly one of `projectSlug` or `projectId`. Setting both, or neither, makes the reconcile fail: `kubectl apply` still succeeds, and the error surfaces in the resource's status conditions.
</Note>
<Note>
If you self-host Infisical, point this CRD at your instance with the optional `spec.hostAPI` field, or set the `hostAPI` Helm value for the operator. When neither is set, the operator's default is `https://app.infisical.com/api`.
</Note>
Apply the resource:
```bash
kubectl apply -f push-secret.yaml
```

The InfisicalDynamicSecret CRD tells the operator to create and lease a Dynamic Secret, then write the lease credentials into a Kubernetes Secret that any workload can consume. Like the push CRD, it is a v1alpha1 resource with its own inline authentication block; the shared connection and auth objects are not used here.

| Name | Where it lives | Value in this tutorial |
|---|---|---|
| Infisical dynamic secret name | Set in the Infisical UI above | `dynamic-secret-lease` |
| CRD `dynamicSecret.secretName` | Must equal the Infisical name | `dynamic-secret-lease` |
| CRD `managedSecretReference.secretName` | The Kubernetes Secret the operator creates | `dynamic-secret-test` |
The verify command reads the Kubernetes Secret, so it uses `managedSecretReference.secretName`.
Save the manifest below as `dynamic-secret.yaml`. Replace `your-identity-id` and `your-project-slug`, and set `environmentSlug` to your confirmed slug.
```yaml
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalDynamicSecret
metadata:
name: dynamic-secret-demo
namespace: default
spec:
dynamicSecret:
secretName: dynamic-secret-lease
projectSlug: your-project-slug
environmentSlug: prod
secretsPath: "/"
authentication:
kubernetesAuth:
identityId: your-identity-id
serviceAccountRef:
name: infisical-service-account
namespace: default
managedSecretReference:
secretName: dynamic-secret-test
secretNamespace: default
leaseRevocationPolicy: Revoke
leaseTTL: "1h"
```
Note for this CRD:
- The authentication block is the same inline shape as the push CRD, and the environment field is `environmentSlug`. Here `projectSlug`/`environmentSlug`/`secretsPath` live under `spec.dynamicSecret`.
- `leaseRevocationPolicy: Revoke` revokes the lease when you delete the resource, which matters for cleanup. Omit it to leave leases in place.
- `leaseTTL` is optional. When omitted, the operator sends no TTL and the lease uses the dynamic secret's own default TTL configured in Infisical.
- Like the push CRD, it accepts `spec.hostAPI` for self-hosted instances.
Apply the resource:
```bash
kubectl apply -f dynamic-secret.yaml
```
```bash
kubectl get secret dynamic-secret-test -n default -o yaml
```
The `data` section should contain the generated database username and password for the lease. If you renamed `managedSecretReference.secretName`, substitute that name in the command above.

Confirm the shared objects are healthy and all three workflows succeeded:
kubectl get infisicalconnection infisical-connection -n default shows True in the READY column.kubectl get infisicalauth infisical-auth -n default shows True in the READY column.kubectl exec -it $(kubectl get pod -l app=nginx -n default -o jsonpath='{.items[0].metadata.name}') -n default -- env | grep SMTP prints [email protected].API_KEY appears in your Infisical project's environment.kubectl get secret dynamic-secret-test -n default -o jsonpath='{.data}' | jq shows lease credentials.If all five checks pass, you have synced secrets in both directions and generated a leased database credential from Kubernetes.
If a sync does not produce the expected result, inspect the resources' status conditions and the operator logs before changing anything. For the pull path, diagnose in dependency order: connection first, then auth, then the static secret. Each resource only becomes ready after the one it references.
<AccordionGroup> <Accordion title="The InfisicalConnection is not Ready"> Describe the resource and read its conditions:```bash
kubectl describe infisicalconnection infisical-connection -n default
```
The operator sets `secrets.infisical.com/IsReady` from a health check: it calls `<address>/api/status` and expects a successful response. If `IsReady` is `False`, the condition message names the error. Common causes:
- The address is wrong, or the Infisical instance is not reachable from inside the cluster (this direction is the operator calling Infisical).
- `spec.address` is empty and the operator's `INFISICAL_HOST_API` environment variable is not set. The error message says exactly this.
- A self-hosted instance uses a custom CA the operator does not trust. Reference the CA via `spec.tls.caCertificate`.
Healthy connections are re-checked every 5 minutes, so a recovered instance can take a few minutes to show `Ready` again.
```bash
kubectl describe infisicalauth infisical-auth -n default
```
The operator sets `secrets.infisical.com/IsReady` (the login result) and `secrets.infisical.com/AuthMethod` (the method in use). If `IsReady` is `False`, check these causes in order:
- The referenced `InfisicalConnection` is not ready. The auth cannot log in until the connection's `IsReady` condition is `True`; fix the connection first.
- `identityIdRef` points at a missing Secret or key, or the stored Identity ID is wrong. Confirm with `kubectl get secret kubernetes-credentials -n default -o jsonpath='{.data.identityId}' | base64 -d`.
- The Kubernetes Auth method is not fully wired: the **Token Reviewer JWT** was never pasted, or the service-account name or namespace does not match the **Allowed Service Account Names** / **Allowed Namespaces** you configured.
- **The cluster's API server is not reachable from the Infisical instance.** Kubernetes Auth requires the Infisical backend to call your cluster's TokenReview API at the **Kubernetes Host URL**. If you pasted a local address such as `https://127.0.0.1:6443` while using Infisical Cloud, every login fails even though the JWT and names are correct. Revisit the prerequisites.
```bash
kubectl describe infisicalstaticsecret infisical-static-secret-demo -n default
```
The operator sets `secrets.infisical.com/LastReconcileStatus`: `True` with reason `OK` on success, or `False` with a "Reconciliation failed" message naming the error. Common causes:
- The referenced `InfisicalAuth` is not ready. The static secret cannot sync until the auth's `IsReady` condition is `True`; work up the chain.
- A wrong `projectSlug`, `environmentSlug`, or `secretPath` in `sources`, or the Machine Identity lacks access to the project.
- A source sets both or neither of `projectId`/`projectSlug`; exactly one is required.
Then check the operator logs:
```bash
kubectl logs -l control-plane=controller-manager -n default
```
```bash
kubectl describe infisicalpushsecret push-secret-demo -n default
```
Check the `secrets.infisical.com/Authenticated` and `secrets.infisical.com/Reconcile` conditions. Read the `Reconcile` condition's Reason and Message rather than its `True`/`False` status: on failure this condition reports `Status=True` with `Reason=Error`, the opposite of the usual Kubernetes convention, and the message names the error. Confirm `push.secret.secretName` matches an existing Kubernetes Secret and that `destination.projectSlug` and `destination.environmentSlug` are correct. If you self-host, confirm `spec.hostAPI` (or the Helm `hostAPI` value) points at your instance; otherwise the operator targets `https://app.infisical.com/api`.
```bash
kubectl describe infisicaldynamicsecret dynamic-secret-demo -n default
```
The operator sets `secrets.infisical.com/Authenticated`, `secrets.infisical.com/LeaseCreated`, and `secrets.infisical.com/LeaseRenewal`. A failed `LeaseCreated` carries whatever error lease creation returned, commonly an unreachable database or wrong connection credentials. Confirm the database is reachable from your Infisical instance and that the connection details entered in the UI are correct.
Remove the resources this tutorial created so you do not leave an active database lease or orphaned objects behind. Delete consumers before the objects they reference, so nothing reconciles against a missing dependency.
Delete the sync resources. Deleting the InfisicalDynamicSecret with leaseRevocationPolicy: Revoke revokes the active database lease.
kubectl delete infisicaldynamicsecret dynamic-secret-demo -n default
kubectl delete infisicalpushsecret push-secret-demo -n default
kubectl delete infisicalstaticsecret infisical-static-secret-demo -n default
Because the static secret's target used creationPolicy: Owner, the managed managed-secret is deleted along with the CRD.
Delete the auth object, then the connection it references:
kubectl delete infisicalauth infisical-auth -n default
kubectl delete infisicalconnection infisical-connection -n default
Delete the demo app and the remaining secrets, including the Identity ID Secret:
kubectl delete deployment nginx-demo -n default
kubectl delete secret dynamic-secret-test push-secret-demo kubernetes-credentials -n default
Delete the service accounts, their token secrets, and the ClusterRoleBinding:
kubectl delete secret infisical-token-reviewer-token infisical-service-account-token -n default
kubectl delete serviceaccount infisical-token-reviewer infisical-service-account -n default
kubectl delete clusterrolebinding infisical-token-reviewer-binding
Uninstall the operator:
helm uninstall infisical-operator
In Infisical, delete the dynamic secret, the demo Machine Identity, and the demo Project if you no longer need them.