Kubernetes Integration
Sync KeyEnv secrets to Kubernetes using External Secrets Operator (ESO).
Kubernetes Integration
Sync KeyEnv secrets to Kubernetes using the External Secrets Operator (ESO).
Overview
The External Secrets Operator is a Kubernetes operator that synchronizes secrets from external providers into Kubernetes Secrets. KeyEnv provides native ESO webhook endpoints, allowing you to sync your secrets directly into Kubernetes without deploying additional infrastructure.
How It Works
- ESO calls KeyEnv's webhook endpoints to fetch secret values
- KeyEnv authenticates the request using your service token
- ESO creates or updates Kubernetes Secrets with the fetched values
- Secrets are automatically refreshed based on your configured interval
Prerequisites
Before setting up the integration, ensure you have:
- Kubernetes cluster with kubectl access
- External Secrets Operator installed (installation guide)
- KeyEnv service token with read access to your project
Creating a Service Token
- Navigate to your project in the KeyEnv dashboard
- Go to Settings → Service Tokens
- Create a new token with Read scope
- Copy the token (starts with
env_)
Setup
Step 1: Create a Kubernetes Secret with Your KeyEnv Token
Store your KeyEnv service token as a Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: keyenv-token
namespace: external-secrets
stringData:
token: "env_your_service_token_here"Apply with:
kubectl apply -f keyenv-token.yamlStep 2: Create a ClusterSecretStore
Create a ClusterSecretStore that configures ESO to communicate with KeyEnv:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: keyenv
spec:
provider:
webhook:
url: "https://api.keyenv.dev/api/v1/eso/secret?project=<PROJECT_ID>&env={{ .remoteRef.property }}&key={{ .remoteRef.key }}"
result:
jsonPath: "$.value"
headers:
Authorization: "Bearer {{ .auth.token }}"
secrets:
- name: auth
secretRef:
namespace: external-secrets
name: keyenv-token
key: tokenReplace <PROJECT_ID> with your KeyEnv project ID or slug.
Apply with:
kubectl apply -f keyenv-secretstore.yamlStep 3: Create an ExternalSecret
Create an ExternalSecret to sync specific secrets from KeyEnv:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-app-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: keyenv
kind: ClusterSecretStore
target:
name: my-app-secrets
data:
- secretKey: DATABASE_URL
remoteRef:
key: DATABASE_URL
property: production
- secretKey: API_KEY
remoteRef:
key: API_KEY
property: productionThis creates a Kubernetes Secret named my-app-secrets containing:
DATABASE_URL- fetched from KeyEnv'sproductionenvironmentAPI_KEY- fetched from KeyEnv'sproductionenvironment
The property field specifies the KeyEnv environment, and key specifies the secret name.
Bulk Export with dataFrom
To sync all secrets from an environment at once, use the bulk export endpoint with dataFrom.
Create a Bulk ClusterSecretStore
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: keyenv-bulk
spec:
provider:
webhook:
url: "https://api.keyenv.dev/api/v1/eso/secrets?project=<PROJECT_ID>&env={{ .remoteRef.key }}"
result:
jsonPath: "$.data"
headers:
Authorization: "Bearer {{ .auth.token }}"
secrets:
- name: auth
secretRef:
namespace: external-secrets
name: keyenv-token
key: tokenCreate an ExternalSecret with dataFrom
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: all-production-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: keyenv-bulk
kind: ClusterSecretStore
target:
name: production-secrets
dataFrom:
- extract:
key: productionThis syncs all secrets from the production environment into a single Kubernetes Secret named production-secrets.
Environment Inheritance
KeyEnv supports environment inheritance, and this is fully supported by the ESO integration:
- Single secret: If the requested secret is not found in the target environment, the inheritance chain is searched upward to parent environments.
- Bulk export: Returns all secrets from the target environment and its parent chain, with child values taking precedence over parent values for duplicate keys.
If you've explicitly hidden an inherited secret in KeyEnv (overriding without a value), that secret will not be returned by the ESO endpoints for that environment.
API Reference
KeyEnv provides two ESO-compatible webhook endpoints:
GET /api/v1/eso/secret
Fetch a single secret value.
| Parameter | Required | Description |
|---|---|---|
project | Yes | Project ID or slug |
env | Yes | Environment name or ID |
key | Yes | Secret key name |
Response:
{ "value": "secret-value-here" }GET /api/v1/eso/secrets
Fetch all secrets as a key-value map (for ESO dataFrom).
| Parameter | Required | Description |
|---|---|---|
project | Yes | Project ID or slug |
env | Yes | Environment name or ID |
Response:
{
"data": {
"DATABASE_URL": "postgres://...",
"API_KEY": "sk_live_..."
}
}Error Codes
| Status | Description |
|---|---|
| 400 | Missing required parameters or circular inheritance detected |
| 401 | Invalid or missing token |
| 403 | Token lacks access to project |
| 404 | Project, environment, or secret not found |
| 500 | Internal server error |
Troubleshooting
ExternalSecret Status Shows "SecretSyncedError"
Check the ExternalSecret status for details:
kubectl describe externalsecret my-app-secretsCommon causes:
- 401 Unauthorized: Verify your KeyEnv token is correct and hasn't expired
- 403 Forbidden: Ensure the token has read access to the specified project
- 404 Not Found: Check that the secret key and environment name are correct
Secrets Not Updating
- Check the
refreshIntervalon your ExternalSecret - Verify ESO controller is running:
kubectl get pods -n external-secrets - Check ESO controller logs:
kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets
ClusterSecretStore Shows "NotReady"
Validate the ClusterSecretStore status:
kubectl describe clustersecretstore keyenvCommon issues:
- Token secret not found in the specified namespace
- Incorrect secret key name (should be
token) - Network connectivity issues to
api.keyenv.dev
Testing the Connection
Test the KeyEnv endpoint directly:
curl -H "Authorization: Bearer env_your_token" \
"https://api.keyenv.dev/api/v1/eso/secret?project=my-project&env=production&key=DATABASE_URL"A successful response returns:
{ "value": "your-secret-value" }Best Practices
- Use dedicated service tokens - Create a token specifically for Kubernetes with minimal permissions
- Set appropriate refresh intervals - Balance between freshness and API load (1h is a good default)
- Use
dataFromfor many secrets - More efficient than individual secret syncs - Monitor ESO status - Set up alerts for sync failures
- Test in non-production first - Verify your setup works before deploying to production