Skip to main content

Kubernetes Secrets Manager

OpenMetadata can use Kubernetes Secrets as its secrets manager backend, storing sensitive values (passwords, tokens, keys, etc.) as native K8s Secret objects instead of encrypted fields in the database.

1. Permissions (Kubernetes RBAC)

The OpenMetadata Server needs RBAC access to Kubernetes Secret objects in the target namespace. The Python ingestion runtime only needs read access.

Required verbs

ComponentVerbs
OpenMetadata Server (Java)create, get, update, delete
Ingestion / Airflow (Python)get

Example: Role + RoleBinding (namespace-scoped)

apiVersion: v1
kind: ServiceAccount
metadata:
 name: openmetadata-secrets-sa
 namespace: <om-namespace>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
 name: openmetadata-secrets-role
 namespace: <secrets-namespace>
rules:
 - apiGroups: [""]
   resources: ["secrets"]
   verbs: ["create", "get", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
 name: openmetadata-secrets-rb
 namespace: <secrets-namespace>
subjects:
 - kind: ServiceAccount
   name: openmetadata-secrets-sa
   namespace: <om-namespace>
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: Role
 name: openmetadata-secrets-role
If the ingestion runtime runs in a separate ServiceAccount, grant it a read-only Role with only get.
If OpenMetadata and the target secrets live in the same namespace, <om-namespace> and <secrets-namespace> are the same.

2. Update configuration

2.1 OpenMetadata Server (openmetadata.yaml)

secretsManagerConfiguration:
 secretsManager: kubernetes                         # or env var SECRET_MANAGER
 prefix: ${SECRET_MANAGER_PREFIX:-""}               # Optional prefix for secret names
 tags: ${SECRET_MANAGER_TAGS:-[]}                   # Labels added to K8s Secrets, format: [key1:value1,key2:value2,...]
 parameters:
   namespace: ${OM_SM_NAMESPACE:-"default"}
   kubeconfigPath: ${OM_SM_KUBECONFIG_PATH:-""}
   inCluster: ${OM_SM_IN_CLUSTER:-"false"}
ParameterEnv varDescription
secretsManagerSECRET_MANAGERSet to kubernetes
prefixSECRET_MANAGER_PREFIXOptional prefix prepended to all secret names
tagsSECRET_MANAGER_TAGSKey-value pairs added as K8s labels on created Secrets. Format: [key1:value1,key2:value2,...]
namespaceOM_SM_NAMESPACENamespace where secrets are stored (default: default)
kubeconfigPathOM_SM_KUBECONFIG_PATHPath to a kubeconfig file (out-of-cluster only)
inClusterOM_SM_IN_CLUSTERUse in-cluster ServiceAccount auth (default: false)

Choosing inCluster vs kubeconfigPath

  • In-cluster (recommended for K8s deployments): set OM_SM_IN_CLUSTER=true. Uses the pod’s ServiceAccount and the RBAC from section 1. Leave kubeconfigPath empty.
  • Out-of-cluster: set OM_SM_IN_CLUSTER=false and OM_SM_KUBECONFIG_PATH to a kubeconfig file readable by the OpenMetadata process. If the kubeconfig path is also empty, the default kubeconfig (~/.kube/config) is used.

2.2 Pipeline Service Client

In the pipelineServiceClientConfiguration section of openmetadata.yaml, set the secrets manager loader so the ingestion framework knows how to authenticate:
pipelineServiceClientConfiguration:
 secretsManagerLoader: ${PIPELINE_SERVICE_CLIENT_SECRETS_MANAGER_LOADER:-"noop"}
Supported values: noop, airflow, env.

2.3 Airflow configuration (self-managed Airflow)

If you run ingestion via your own Airflow deployment, configure it to resolve K8s secrets. Option A: airflow.cfg
[openmetadata_secrets_manager]
kubernetes_namespace = <secrets-namespace>
kubernetes_kubeconfig_path =
kubernetes_in_cluster = true
Option B: Airflow environment variables Airflow auto-maps env vars with the pattern AIRFLOW__<SECTION>__<KEY>:
AIRFLOW__OPENMETADATA_SECRETS_MANAGER__KUBERNETES_NAMESPACE=<secrets-namespace>
AIRFLOW__OPENMETADATA_SECRETS_MANAGER__KUBERNETES_KUBECONFIG_PATH=
AIRFLOW__OPENMETADATA_SECRETS_MANAGER__KUBERNETES_IN_CLUSTER=true
If Airflow runs inside the cluster, use KUBERNETES_IN_CLUSTER=true and ensure Airflow’s ServiceAccount has get permissions on Secrets in the target namespace.

2.4 Non-Airflow ingestion (env loader)

When using the env secrets manager loader (e.g., standalone ingestion containers), configure via plain environment variables:
KUBERNETES_NAMESPACE=<secrets-namespace>
KUBERNETES_IN_CLUSTER=true
KUBERNETES_KUBECONFIG_PATH=  # leave empty for in-cluster
The Python env loader also auto-detects the current namespace from /var/run/secrets/kubernetes.io/serviceaccount/namespace when running in-cluster, falling back to default.

3. Migrate secrets and restart services

After updating configuration, migrate existing sensitive values from database encryption to Kubernetes Secrets:
./bootstrap/openmetadata-ops.sh migrate-secrets
This migrates secrets from the DB to the configured Secrets Manager. It does not support migrating between external Secrets Managers (e.g., from AWS SSM to Kubernetes). Then restart:
  1. OpenMetadata Server — so it reads/writes secrets to K8s.
  2. Airflow / ingestion runtime — so it picks up the secrets manager settings.

4. Workflow YAML (self-managed Airflow)

If you use your own Airflow to run ingestion workflows, configure the workflow YAML:
workflowConfig:
 openMetadataServerConfig:
   secretsManagerProvider: kubernetes
   secretsManagerLoader: airflow     # or "env" for non-Airflow
   hostPort: <OpenMetadata host and port>
   authProvider: <OpenMetadata auth provider>

5. How secrets are stored

Naming convention

Secret names use hyphens as separators (not slashes) to comply with Kubernetes DNS naming rules:
<prefix>-<clusterName>-<path>-<components>
  • prefix comes from secretsManagerConfiguration.prefix
  • clusterName comes from the top-level clusterName in openmetadata.yaml (default: openmetadata)
  • The remaining path components are derived from the entity/connection being stored
Examples (assuming default clusterName: openmetadata, no prefix):
openmetadata-bot-name-config-jwttoken
openmetadata-database-myservice-password
Names are sanitized for Kubernetes compatibility:
  • Only lowercase alphanumeric characters and hyphens are allowed
  • Consecutive hyphens are collapsed to a single hyphen
  • Leading and trailing hyphens are stripped
  • Truncated to 253 characters (K8s Secret name limit)

Data format

Each secret is stored as a Kubernetes Secret object with:
  • A single data key: value — containing the secret as UTF-8 bytes
  • Labels:
  • app: openmetadata
  • managed-by: openmetadata-secrets-manager
  • Plus any custom labels from the tags configuration
apiVersion: v1
kind: Secret
metadata:
 name: openmetadata-database-myservice-password
 namespace: default
 labels:
   app: openmetadata
   managed-by: openmetadata-secrets-manager
data:
 value: <base64-encoded-secret>