External Secrets
External Secrets Operator reads secrets from an external secret manager and writes them into Kubernetes Secrets on your cluster. On the cluster’s Secrets page you connect a secret store, choose what to sync, and add the resulting Secret to a deployment. The operator runs in your cluster and reads the values there. Edka keeps no copy of them.
Path: Clusters → your cluster → Secrets
- A secret store is a connection to one provider, with the credentials to read from it.
- A synced secret reads from a store and keeps one Kubernetes Secret up to date.
- A deployment or cron job reads that Secret as environment variables or mounts it as files.
Prerequisites
Section titled “Prerequisites”- An Edka cluster. External Secrets does not need to be installed first. Connecting the first store installs it.
- A credential on the provider side, created as the provider’s section below describes. Stores use static credentials, such as an access key, a token or a service account key. Cloud workload identity, such as IAM roles for service accounts or GCP Workload Identity Federation, is not available on Hetzner or satellite clusters.
Connect a store
Section titled “Connect a store”- On the Secrets page, open the Stores tab and click Connect store.
- Enter a Name. Synced secrets refer to the store by this name. Use lowercase letters, numbers and dashes, up to 53 characters.
- Choose the Provider, then fill in its Connection fields and Credentials. Providers lists them.
- Under Access, choose the Namespaces that may use the store. Leave it empty to let every namespace use it.
- Click Connect store.
The store’s page opens. It shows Waiting until the operator has checked the credentials, then Connected with a Sync a secret button. When External Secrets is not on the cluster yet, Edka installs the External Secrets add-on with the first store, and the store waits until the operator is running.
Credentials are write-only. Edka writes them into the cluster and never shows them again. To replace a credential, open the store, enter the new value and click Save. A credential left blank keeps the stored value. The name and provider of a store are fixed once it is connected.
Namespaces decides which namespaces may sync from the store, not which remote secrets they can read. Anyone who can create a synced secret in an allowed namespace can sync everything the credential can read. Limit the credential on the provider side to what the cluster needs.
Providers
Section titled “Providers”| Provider | Credentials |
|---|---|
| AWS Secrets Manager, AWS Parameter Store | Access key ID and secret access key |
| Google Secret Manager | Service account JSON key |
| Azure Key Vault | Client ID and client secret of a service principal |
| HashiCorp Vault, OpenBao | Token, or AppRole role ID and secret ID |
| Akeyless | Access ID and access key |
| 1Password | Service account token |
| Doppler | Service token |
| Infisical | Universal Auth client ID and client secret |
| Bitwarden Secrets Manager | Machine account access token |
| Scaleway Secret Manager | API access key and secret key |
| GitLab CI/CD variables | Access token with the read_api scope |
| Other provider | Any, entered as key and value pairs |
AWS Secrets Manager and AWS Parameter Store
Section titled “AWS Secrets Manager and AWS Parameter Store”In AWS IAM, create a user with an access key and give it read access to the secrets or parameters the cluster needs.
For Secrets Manager, allow these actions on the secrets:
secretsmanager:GetSecretValuesecretsmanager:DescribeSecretsecretsmanager:ListSecretVersionIds
To read every secret under a path, also allow secretsmanager:ListSecrets and
secretsmanager:BatchGetSecretValue on *.
For Parameter Store, allow ssm:GetParameter* on the parameters. To match
parameter names with a pattern, also allow ssm:DescribeParameters on *. A
SecureString parameter encrypted with a customer managed KMS key also needs
kms:Decrypt on that key.
| Field | Value |
|---|---|
| Region | AWS region, such as eu-central-1 |
| Role to assume | Optional. ARN of an IAM role that holds the read access |
| Access key ID | Access key of the IAM user |
| Secret access key | Secret of the access key, write-only |
With Role to assume, the access key only needs sts:AssumeRole on that
role. The key stored in the cluster can then do nothing else, and the role’s
permissions decide what the store reads.
Google Secret Manager
Section titled “Google Secret Manager”In Google Cloud, create a service account and grant it the Secret Manager
Secret Accessor role (roles/secretmanager.secretAccessor) on the project or
on the secrets it reads. Create a JSON key for it. To read every secret, the
service account also needs to list secrets, for example through the
Secret Manager Viewer role (roles/secretmanager.viewer) on the project,
and Secret Accessor on every secret that matches.
| Field | Value |
|---|---|
| Service account key | The whole JSON key file, write-only |
| Project ID | Optional. Defaults to the project of the key |
| Location | Optional. Set it only for regional secrets, such as europe-west1 |
Azure Key Vault
Section titled “Azure Key Vault”In Microsoft Entra ID, register an application and create a client secret for it. On the key vault, assign the application the Key Vault Secrets User role. A vault that uses access policies instead of Azure RBAC needs an access policy with the Get and List secret permissions.
| Field | Value |
|---|---|
| Vault URL | Such as https://my-vault.vault.azure.net |
| Tenant ID | Directory (tenant) ID |
| Client ID | Application (client) ID of the registered application |
| Client secret | Client secret of the application, write-only |
HashiCorp Vault and OpenBao
Section titled “HashiCorp Vault and OpenBao”HashiCorp Vault and
OpenBao use the same form. A store reads from one
key/value secrets engine, version 1 or 2. Create a policy that can read the
secrets the cluster needs. For KV version 2 mounted at secret:
path "secret/data/production/*" { capabilities = ["read"]}
path "secret/metadata/production/*" { capabilities = ["read", "list"]}The metadata rule is only needed to read every secret under a path. Then create a
token with this policy, or an AppRole that has it.
| Field | Value |
|---|---|
| Server | URL with port, such as https://vault.example.com:8200 |
| KV mount | Mount path of the KV engine without a leading slash. Default secret |
| KV version | v2 or v1 |
| Namespace | Optional. Vault Enterprise or OpenBao namespace |
| CA certificate | Optional. PEM certificate of the private CA that signed the server certificate |
| Authentication | Token, or AppRole with AppRole mount (default approle), Role ID and Secret ID |
A token stops working when its TTL ends, and the store then shows Not connected. With AppRole, the operator signs in again with the role ID and secret ID.
Akeyless
Section titled “Akeyless”In Akeyless, create an API Key authentication method and give it read access to the secrets through an access role.
| Field | Value |
|---|---|
| Gateway URL | Default https://api.akeyless.io, or the URL of your own gateway |
| Access ID | Starts with p- |
| Access key | Write-only |
1Password
Section titled “1Password”In 1Password, create a service account with read access to the vault the cluster needs, and copy its token.
| Field | Value |
|---|---|
| Vault | Name or ID of the vault |
| Service account token | Starts with ops_ |
A remote secret is an item. With One secret, every key, each field
of the item becomes a key. With Selected keys, a Property picks one
field, such as password. 1Password stores do not offer
Every secret, as keys of one Secret.
Doppler
Section titled “Doppler”In Doppler, open the config the cluster reads and create a service token on its Access tab. A service token reads one config of one project, so connect one store per config.
| Field | Value |
|---|---|
| Service token | Starts with dp.st. |
To sync the whole config into one Secret, use Every secret, as keys of one Secret with an empty name pattern.
Infisical
Section titled “Infisical”In Infisical, create a machine identity with Universal Auth and a client secret. Add the identity to the project with a role that can read secrets in the environment.
| Field | Value |
|---|---|
| Host | Default https://app.infisical.com. https://eu.infisical.com for the EU region, or your own instance |
| Project slug | Slug of the project |
| Environment slug | Such as prod |
| Secrets path | Folder to read from. Default / |
| Client ID | Universal Auth client ID of the machine identity |
| Client secret | Universal Auth client secret, write-only |
To sync a whole environment into one Secret, use Every secret, as keys of one Secret with no path and an empty name pattern.
Bitwarden Secrets Manager
Section titled “Bitwarden Secrets Manager”In Bitwarden Secrets Manager, create a machine account, give it read access to a project, and create an access token for it.
| Field | Value |
|---|---|
| Region | bitwarden.com or bitwarden.eu |
| Organization ID | ID of the organization |
| Project ID | ID of the project |
| Access token | Machine account token, write-only |
External Secrets talks to Bitwarden through a Bitwarden SDK server. Edka runs
it in the external-secrets namespace with a certificate from
cert-manager. When cert-manager is missing from the
cluster, connecting the store asks you to install it from Add-ons first.
Remote secrets are referenced by ID or name and have no Property.
Scaleway Secret Manager
Section titled “Scaleway Secret Manager”In Scaleway IAM, create an application with an API key, and give it a policy that can read secrets and access their versions in the project.
| Field | Value |
|---|---|
| Region | Paris fr-par, Amsterdam nl-ams or Warsaw pl-waw |
| Project ID | ID of the project that holds the secrets |
| Access key | Starts with SCW |
| Secret key | Write-only |
Remote keys start with name:, id: or path:, such as name:database-url.
GitLab CI/CD variables
Section titled “GitLab CI/CD variables”A GitLab store reads the CI/CD variables of one project. Create a project or
personal access token with the read_api scope. GitLab shows CI/CD variables
only to members with at least the Maintainer role, so the token needs that
role.
| Field | Value |
|---|---|
| GitLab URL | Default https://gitlab.com, or your own instance |
| Project ID | Numeric ID of the project |
| Environment scope | Optional. Reads only variables of this scope |
| Access token | Starts with glpat- |
Other providers
Section titled “Other providers”External Secrets supports more providers than the forms above. The External Secrets provider docs list them. To use one of them, choose Other provider.
- In Provider, paste the
spec.providerblock of a store from that provider’s page. - Under Credentials, click Add credential for each secret value the block needs, with a key and a value.
- In the block, write every secret reference with only
key, set to the matching credential key. Leave outname. Edka fills it in. A CA certificate read from a Secret works the same way, ascaProviderwithtype: Secretandkey.
A store reads only the credentials entered here. Saving refuses a secret
reference that names another Secret, and the error names its path in the
block. Service account
references, such as serviceAccountRef for Vault Kubernetes auth, stay as
written.
For Pulumi ESC, with a credential named
access-token:
pulumi: organization: acme project: platform environment: production auth: accessToken: secretRef: key: access-tokenSync a secret
Section titled “Sync a secret”- Click Sync a secret on a connected store, in its row on the Stores tab or on its page. The Sync a secret button on the Secrets tab works too.
- Under From the store, check the Store and choose what to Read, described below. The default depends on the provider.
- Under Into the cluster, check the Name of the Kubernetes Secret and
choose its Namespace. Until you change it, the name follows the remote
secret or path you enter, or is the store’s name followed by
-secretswhen you read every secret. The namespace must not already have a Secret with that name, and a store limited to other namespaces is refused. To sync a secret before the deployment that reads it, choose Custom namespace and type the deployment’s namespace. Edka creates it with the synced secret. - Choose how often to Read the store.
- Check the summary line above the button, then click Create synced secret.
The synced secret’s page opens with Waiting, then Synced once the operator has read the provider.
One secret, every key
Section titled “One secret, every key”Enter one Remote secret. Each of its keys becomes a key of the Secret.
An AWS Secrets Manager secret production/api that holds
{"DATABASE_URL": "...", "REDIS_URL": "..."} becomes a Secret with the keys
DATABASE_URL and REDIS_URL.
Selected keys
Section titled “Selected keys”Add one row per key of the Secret. Secret key is the key name in the Kubernetes Secret, and Remote key names the remote secret it reads. For a remote secret that holds several fields, such as a JSON secret in AWS or a 1Password item, Property picks one field.
| Secret key | Remote key | Property |
|---|---|---|
DATABASE_URL | production/db | url |
STRIPE_KEY | production/stripe |
Every secret, as keys of one Secret
Section titled “Every secret, as keys of one Secret”Reads every remote secret that matches into one Kubernetes Secret, with one key per remote secret. The store’s credentials must be allowed to list secrets. The provider sections above name the permission.
- Path, where the provider has paths, reads only the secrets under it. The path is left out of the key names.
- Name pattern is a regular expression on secret names. Empty reads every secret.
- Slashes in the remaining names become underscores, since Secret keys can’t hold slashes.
With AWS Parameter Store and the path /production/api, the parameter
/production/api/DATABASE_URL becomes the key DATABASE_URL, and
/production/api/stripe/KEY becomes stripe_KEY. With Doppler, an empty name
pattern copies the whole config, and ^STRIPE_ copies only the names that start
with STRIPE_.
A Kubernetes Secret holds at most 1 MiB. On a store with many secrets, narrow the list with a path or a name pattern, or sync several Secrets.
Refresh
Section titled “Refresh”Read the store sets how often the operator reads the provider: every minute, 5 minutes, 15 minutes, hour, 6 hours, day, or once. The default is every hour. Each read calls the provider’s API, and some providers bill or rate-limit per call.
The synced secret page
Section titled “The synced secret page”Click a synced secret in the table to open its page. It shows:
- the state, when the provider was last read, and the error message when a sync fails
- the keys of the Secret, with the values masked. Owners and admins can show them, as Show secret values describes
- Used by, the deployments and cron jobs that read the Secret
- Sync now, which reads the provider right away
- the store, read mode and refresh settings, which you can change
- Delete synced secret
The name and namespace are fixed once the synced secret exists.
Use it in a deployment
Section titled “Use it in a deployment”A synced secret is an ordinary Kubernetes Secret in its namespace. A deployment in the same namespace can read it in two ways.
- Environment variables. On the Envs & Secrets tab, under Secrets from the cluster, select the Secret and click Add. Every key becomes an environment variable. A variable or secret entered on the deployment wins over a key with the same name. Pods read the values when they start. Turn on Restart pods when a Secret changes to roll them when the synced secret changes, which needs edka-agent 0.0.14 or later.
- Files. On the Storage tab, add a volume of type Secret, pick the Secret, and enter a mount path. Each key becomes a read-only file in the mount path. The files update when the Secret changes, without a restart.
See Deployments.
Status
Section titled “Status”| Status | For | Meaning |
|---|---|---|
| Connected | Store | The operator signed in to the provider with the credentials |
| Waiting | Store | External Secrets is still installing, or the operator has not checked the credentials yet |
| Not connected | Store | The provider refused the credentials or could not be reached. Hover for the error |
| Synced | Synced secret | The last read succeeded, with the time of that read |
| Waiting | Synced secret | The operator has not read the provider yet |
| Sync failing | Synced secret | The last read failed. Hover for the error |
A Secret whose sync fails keeps its last values, so running pods are not affected. A synced secret that never synced has no Secret yet, and pods that read it can’t start until the first sync succeeds.
When a store shows Not connected, every synced secret that uses it stops updating. Common causes:
- The credential expired or was revoked. Open the store, enter a new value and click Save.
- A connection field has the wrong region, URL, project or vault name.
- A private CA signed the server certificate. Paste the CA in CA certificate.
When a synced secret shows Sync failing while its store is connected, the remote secret or property does not exist, or the credential has no access to it. With Every secret, as keys of one Secret, the credential may also lack the permission to list secrets, such as a Google service account without Secret Manager Viewer. The synced secret’s page shows the provider’s error. Fix the name in the synced secret’s settings or the permissions on the provider side, then click Sync now.
Notifications
Section titled “Notifications”When the cluster runs VictoriaMetrics, Edka installs the External Secrets alert pack once External Secrets is installed. It has two rules:
- Secret sync failing. A synced secret has failed to sync for 10 minutes.
- Secret store not connected. A store has failed its check for 10 minutes.
Both cover stores and synced secrets created outside Edka too, and notify like other alert packs. See External Secrets alert pack.
Stores and secrets created outside Edka
Section titled “Stores and secrets created outside Edka”Stores created with GitOps or kubectl, as ClusterSecretStore or
SecretStore resources, appear on the Stores tab with their status, as
“Created outside Edka”. Edka does not change them. Change them where
they were created.
Synced secrets can read from these stores. A ClusterSecretStore serves the
namespaces it allows. A SecretStore serves only its own namespace.
ExternalSecret resources created outside Edka do not appear in the
Secrets table. Explorer lists them under
External Secrets, together with SecretStore, ClusterSecretStore,
ClusterExternalSecret and PushSecret resources, their state and events.
External Secrets installed outside Edka works when it serves the
external-secrets.io/v1 API, version 0.16 or later. An older install shows a
message to upgrade it.
Remove a synced secret or a store
Section titled “Remove a synced secret or a store”- Synced secret. Open it and click Delete. Edka stops the sync and removes the Kubernetes Secret. The values in the provider stay. Deleting is refused while a deployment or cron job reads the Secret. Remove it from the ones Used by lists first.
- Store. Open it and click Delete. Edka removes the store and its credentials from the cluster. The secrets in the provider stay. Deleting is refused while synced secrets use the store.
External Secrets stays installed after the last store is deleted. Uninstalling the add-on deletes every store and synced secret on the cluster, and with them the Kubernetes Secrets that deployments read. Edka refuses to uninstall it while any exist, including ones created with GitOps or kubectl. Its card under Cluster Settings → Add-ons lists them as dependents. Delete the synced secrets, then the stores, first.
Troubleshooting
Section titled “Troubleshooting”Each synced secret is an ExternalSecret with the same name, in the same
namespace. Each store Edka connects is a ClusterSecretStore with the store’s
name, and its credentials live in the Secret edka-store-<name> in the
external-secrets namespace.
kubectl describe externalsecret api-env -n productionkubectl describe clustersecretstore aws-productionThe events at the end of the output show the provider’s error.
Edka refuses Connect store while External Secrets uninstalls. Connect the store once the add-on is gone, and Edka installs it again.
When the Stores tab says External Secrets is installed but its CRDs are missing, the operator’s resources were removed outside Edka. Uninstall the add-on under Cluster Settings → Add-ons and install it again.
For a Bitwarden store on an External Secrets install made outside Edka, turn on
the Bitwarden SDK server in that install with
bitwarden-sdk-server.enabled=true, in the external-secrets namespace.