Skip to content
SYS.DOCS // DOCS

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.
  • 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.
  1. On the Secrets page, open the Stores tab and click Connect store.
  2. Enter a Name. Synced secrets refer to the store by this name. Use lowercase letters, numbers and dashes, up to 53 characters.
  3. Choose the Provider, then fill in its Connection fields and Credentials. Providers lists them.
  4. Under Access, choose the Namespaces that may use the store. Leave it empty to let every namespace use it.
  5. 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.

ProviderCredentials
AWS Secrets Manager, AWS Parameter StoreAccess key ID and secret access key
Google Secret ManagerService account JSON key
Azure Key VaultClient ID and client secret of a service principal
HashiCorp Vault, OpenBaoToken, or AppRole role ID and secret ID
AkeylessAccess ID and access key
1PasswordService account token
DopplerService token
InfisicalUniversal Auth client ID and client secret
Bitwarden Secrets ManagerMachine account access token
Scaleway Secret ManagerAPI access key and secret key
GitLab CI/CD variablesAccess token with the read_api scope
Other providerAny, 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:GetSecretValue
  • secretsmanager:DescribeSecret
  • secretsmanager: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.

FieldValue
RegionAWS region, such as eu-central-1
Role to assumeOptional. ARN of an IAM role that holds the read access
Access key IDAccess key of the IAM user
Secret access keySecret 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.

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.

FieldValue
Service account keyThe whole JSON key file, write-only
Project IDOptional. Defaults to the project of the key
LocationOptional. Set it only for regional secrets, such as europe-west1

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.

FieldValue
Vault URLSuch as https://my-vault.vault.azure.net
Tenant IDDirectory (tenant) ID
Client IDApplication (client) ID of the registered application
Client secretClient secret of the application, write-only

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.

FieldValue
ServerURL with port, such as https://vault.example.com:8200
KV mountMount path of the KV engine without a leading slash. Default secret
KV versionv2 or v1
NamespaceOptional. Vault Enterprise or OpenBao namespace
CA certificateOptional. PEM certificate of the private CA that signed the server certificate
AuthenticationToken, 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.

In Akeyless, create an API Key authentication method and give it read access to the secrets through an access role.

FieldValue
Gateway URLDefault https://api.akeyless.io, or the URL of your own gateway
Access IDStarts with p-
Access keyWrite-only

In 1Password, create a service account with read access to the vault the cluster needs, and copy its token.

FieldValue
VaultName or ID of the vault
Service account tokenStarts 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.

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.

FieldValue
Service tokenStarts with dp.st.

To sync the whole config into one Secret, use Every secret, as keys of one Secret with an empty name pattern.

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.

FieldValue
HostDefault https://app.infisical.com. https://eu.infisical.com for the EU region, or your own instance
Project slugSlug of the project
Environment slugSuch as prod
Secrets pathFolder to read from. Default /
Client IDUniversal Auth client ID of the machine identity
Client secretUniversal 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.

In Bitwarden Secrets Manager, create a machine account, give it read access to a project, and create an access token for it.

FieldValue
Regionbitwarden.com or bitwarden.eu
Organization IDID of the organization
Project IDID of the project
Access tokenMachine 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.

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.

FieldValue
RegionParis fr-par, Amsterdam nl-ams or Warsaw pl-waw
Project IDID of the project that holds the secrets
Access keyStarts with SCW
Secret keyWrite-only

Remote keys start with name:, id: or path:, such as name:database-url.

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.

FieldValue
GitLab URLDefault https://gitlab.com, or your own instance
Project IDNumeric ID of the project
Environment scopeOptional. Reads only variables of this scope
Access tokenStarts with glpat-

External Secrets supports more providers than the forms above. The External Secrets provider docs list them. To use one of them, choose Other provider.

  1. In Provider, paste the spec.provider block of a store from that provider’s page.
  2. Under Credentials, click Add credential for each secret value the block needs, with a key and a value.
  3. In the block, write every secret reference with only key, set to the matching credential key. Leave out name. Edka fills it in. A CA certificate read from a Secret works the same way, as caProvider with type: Secret and key.

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-token
  1. 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.
  2. Under From the store, check the Store and choose what to Read, described below. The default depends on the provider.
  3. 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 -secrets when 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.
  4. Choose how often to Read the store.
  5. 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.

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.

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 keyRemote keyProperty
DATABASE_URLproduction/dburl
STRIPE_KEYproduction/stripe

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.

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.

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.

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.

StatusForMeaning
ConnectedStoreThe operator signed in to the provider with the credentials
WaitingStoreExternal Secrets is still installing, or the operator has not checked the credentials yet
Not connectedStoreThe provider refused the credentials or could not be reached. Hover for the error
SyncedSynced secretThe last read succeeded, with the time of that read
WaitingSynced secretThe operator has not read the provider yet
Sync failingSynced secretThe 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.

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

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

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.

Terminal window
kubectl describe externalsecret api-env -n production
kubectl describe clustersecretstore aws-production

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