sidebar_label: Secrets in Configuration title: Secrets in Configuration sidebar_position: 4

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

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