Secrets in Configuration
Configuration values such as credentials for lake catalogs or object storage do not need to be
written into server.yaml (or client configuration) as literals. Instead, a value can be an
indirection marker that points at an external source, and Fluss resolves it at load time:
config.providers: directory,env
config.providers.directory.param.allowed.paths: /etc/fluss/secrets
# read from the file /etc/fluss/secrets/s3-secret-key
datalake.paimon.s3.secret-key: ${directory:/etc/fluss/secrets:s3-secret-key}
# read from an environment variable
datalake.paimon.s3.access-key: ${env:PAIMON_S3_ACCESS_KEY}
This keeps the configuration file free of secret material, so it can be checked into version
control or shipped as a plain Kubernetes ConfigMap while the secrets live in a mounted
Secret volume or injected environment variables.
Markers
A marker has the form ${provider:[path:]key} and must be the entire configuration value;
markers embedded inside larger values are not resolved. To use a literal value that starts with
${, escape it by doubling the dollar sign: $${literal} becomes ${literal}. The escape, like
marker resolution, only applies when config.providers is set.
Resolution happens once, when the configuration is loaded: on server startup, when a client
connection is created via ConnectionFactory, and when the lake tiering service parses its
arguments. Rotating a secret requires a restart of the affected process. Markers are rejected in
dynamically altered cluster configuration. If a value looks like a marker but config.providers
is not set, it is kept as a literal and a warning is logged.
Providers
Providers are enabled explicitly with config.providers; a marker referencing a provider that is
not declared fails the startup. Provider parameters are passed as
config.providers.<identifier>.param.<parameter>.
| Provider | Marker | Description |
|---|---|---|
directory | ${directory:/dir:file} | Reads the content of /dir/file verbatim (UTF-8). Matches a Kubernetes Secret mounted as a volume, where each secret key becomes a file. |
env | ${env:VAR} | Reads an environment variable, e.g. one injected via secretKeyRef. |
file | ${file:/path/creds.properties:key} | Reads a single property from a Java properties file. |
The filesystem-reading providers (directory, file) require the allowed.paths parameter — a
comma-separated list of roots they may read from. A marker resolving to a file outside every root
is rejected, so a stray marker cannot read arbitrary files. The env provider optionally accepts
allowlist.pattern, a regular expression the variable name must match.
Custom providers can be added by implementing
org.apache.fluss.config.provider.ConfigProvider and registering the implementation for
Java ServiceLoader discovery. The provider must be visible to the classloader that loads Fluss
itself: place the jar on the server classpath (lib/), or bundle it with the Fluss connector for
Flink jobs. Plugin directories are not scanned.
Kubernetes example
When deploying with the Helm chart, the secrets.mounts / secrets.env values generate all of
the below — see
Deploying with Helm.
# server.yaml shipped as a plain ConfigMap — contains no secrets
config.providers: directory
config.providers.directory.param.allowed.paths: /etc/fluss/secrets
datalake.paimon.s3.secret-key: ${directory:/etc/fluss/secrets:s3-secret-key}
# pod spec: the secret is a normal Secret volume, never part of the ConfigMap
volumes:
- name: fluss-secrets
secret:
secretName: fluss-paimon-creds
containers:
- name: tablet-server
volumeMounts:
- name: fluss-secrets
mountPath: /etc/fluss/secrets
readOnly: true
Values resolved through a provider are treated as sensitive and are hidden when the configuration is logged, regardless of the key name.