Skip to main content
Version: v0.25.0 (Latest)

Configuration Providers

The configuration system uses property sources to supply configuration values. Multiple sources can be active simultaneously, with values resolved by priority order. The IDK ships with local sources; the EDK adds cloud and secret vault providers, and the VDX platform adds database persistence.

Available Providers

IDK (Always Available)

ProviderSource NamePriorityDescription
Environment VariablesenvHighestSystem environment variables with key normalization
Properties Filesproperties-file-{scope}Lowapplication-{profile}.properties per scope
YAML Filesyaml-{scope}Lowapplication.yml with profile support. Separate lib-conf-yaml module, multiplatform (JVM, Android, iOS, JS, WASM, Linux)
Multiplatform Settingsmultiplatform-settings-{scope}LowKache/kmp-settings for cross-platform persistence
Programmatic Mapscustom nameConfigurableCode-defined MapPropertySource or MutableMapPropertySource

Properties and YAML files follow the same integration pattern: profile selection via filename suffix, scoped directory structure, and classpath fallback. The YAML module (lib-conf-yaml) uses snakeyaml-engine-kmp for multiplatform YAML parsing and flattens nested YAML keys to dot notation.

EDK (Add Dependency)

ProviderProvider IDPriorityDescription
Azure App Configurationazure-app-configHighCloud configuration from Azure
REST Config Clientrest-configHighConfiguration from a REST API server
AWS Secrets Manageraws-secrets-Secret resolution (see Secrets)
Azure Key Vaultazure-keyvault-secrets-Secret resolution
HashiCorp Vaultvault-secrets-Secret resolution

VDX (Database Persistence)

ProviderProvider IDPriorityDescription
PostgreSQLpostgresql-dbMediumPersisted settings in PostgreSQL
MySQLmysql-dbMediumPersisted settings in MySQL
SQLitesqlite-dbMediumPersisted settings in SQLite

Auto-Registration

Providers beyond the IDK built-ins use auto-registration. When you add a provider module to your classpath, it automatically integrates with the ConfigService. No wiring code needed; just add the dependency and provide connection details.

build.gradle.kts
dependencies {
// Adding this dependency automatically enables Azure App Configuration
implementation("com.sphereon.edk:lib-conf-azure-app-config:$version")

// Adding this enables database-backed settings
implementation("com.sphereon.vdx:vdx-conf-settings-persistence-postgresql:$version")
}

How It Works

When your application starts, the PropertySourceBootstrap service:

  1. Discovers all PropertySourceContribution implementations via dependency injection
  2. Checks if each contribution is enabled (configuration flag + required config present)
  3. Filters by ConfigLevel to register each source at the correct scope (APP, TENANT, or PRINCIPAL)
  4. Registers enabled sources with the appropriate ConfigService in priority order

Enabling and Disabling Providers

Each auto-registered provider has a unique ID used to control whether it's active:

ProviderProvider IDEnable/Disable Key
Azure App Configazure-app-configconfig.providers.azure-app-config.enabled
REST Config Clientrest-configconfig.providers.rest-config.enabled
PostgreSQL Databasepostgresql-dbconfig.providers.postgresql-db.enabled
MySQL Databasemysql-dbconfig.providers.mysql-db.enabled
SQLite Databasesqlite-dbconfig.providers.sqlite-db.enabled

Providers are enabled by default when their module is on the classpath. To disable one:

# Disable Azure App Configuration
export CONFIG_PROVIDERS_AZURE_APP_CONFIG_ENABLED=false

# Disable database provider
export CONFIG_PROVIDERS_POSTGRESQL_DB_ENABLED=false

Conditional Enablement

Some providers only enable themselves when required configuration is present. For example, Azure App Configuration only registers if connection details are provided:

# Azure provider enables itself when these are set
export AZURE_APPCONFIG_CONNECTION_STRING=Endpoint=https://myconfig.azconfig.io;...

# Or using endpoint + managed identity
export AZURE_APPCONFIG_ENDPOINT=https://myconfig.azconfig.io

Built-in Providers (IDK)

These providers are always available without adding extra dependencies.

Environment Variables

Reads all system environment variables. Keys are normalized from uppercase-underscore to dot-notation:

  • API_BASE_URL resolves as api.base.url
  • OAUTH2_CLIENT_ID resolves as oauth2.client.id

Environment variables also support property protection prefixes. Prefix a variable with FINAL_ or PROTECTED_ to restrict how lower scopes can use it:

# Cannot be overridden at TENANT or PRINCIPAL scope
export FINAL_DATABASE_HOST=prod-db.example.com

# Cannot be interpolated from lower scopes
export PROTECTED_API_SECRET=sensitive-value

Priority: Highest (always wins)

Properties Files

Reads application-{profile}.properties from the config directory or classpath. Three scoped variants exist:

ScopeFiles loadedExample path
APPapplication.properties, application-{profile}.propertiesconfig/application-production.properties
TENANTtenant.properties, tenant-{profile}.propertiesconfig/tenant/{tenantId}/tenant-production.properties
PRINCIPALprincipal.properties, principal-{profile}.propertiesconfig/tenant/{tenantId}/principal/{principalId}/principal-production.properties

The base file is always loaded; the profile-specific file is merged on top.

Priority: Low

YAML Files

The lib-conf-yaml module loads application.yml (and profile-specific variants such as application-production.yml) and flattens nested YAML keys to dot notation. It uses snakeyaml-engine-kmp, so it works on all supported Kotlin Multiplatform targets: JVM, Android, iOS, JS, WASM, and Linux. YAML files follow the same conventions as properties files: profile selection via filename suffix and a scoped directory structure with classpath fallback.

config/application.yml
api:
base-url: https://api.example.com
timeout-ms: 30000
config/tenant/{tenantId}/tenant-production.yml
api:
subscription-key: acme-key-123

Priority: Low

Multiplatform Settings

Uses kmp-settings for cross-platform persistent storage. This is the primary persistence mechanism on mobile platforms (Android SharedPreferences, iOS NSUserDefaults). Three scoped variants cover APP, TENANT, and PRINCIPAL levels.

Priority: Low

Programmatic Maps

Add configuration programmatically using MapPropertySource (read-only) or MutableMapPropertySource (read-write):

val defaults = MapPropertySource(
name = "app-defaults",
source = mapOf(
"http.timeout.ms" to 30000,
"retry.max.attempts" to 3
)
)
configService.addPropertySource(defaults)

Priority: Configurable (defaults to Medium)

Provider Priority

When the same key exists in multiple providers, the highest-priority source wins:

Environment Variable: api.url=https://env.example.com     ← WINS
Cloud Config: api.url=https://azure.example.com
Database: api.url=https://db.example.com
Properties File: api.url=https://file.example.com

Priority is controlled by the order value on each PropertySource (lower number = higher priority). The built-in sources use the Order enum:

Order constantPriorityUsed by
Order.HIGHESTHighestEnvironment variables
Order.HIGHHighCloud providers
Order.MEDIUMMediumProgrammatic maps
Order.LOWLowProperties files, YAML files, multiplatform settings
Order.LOWESTLowestFallback defaults

Caching

Different provider types use different caching strategies:

Provider TypeCachePurpose
Cloud Providers (EDK)OfflineConfigCachePersists config to disk for network failure resilience
Database Providers (VDX)SettingsCache (Kache)In-memory LRU cache with TTL to reduce database queries
IDK Resolution PipelineSyncConfigSnapshotCacheIn-memory snapshot cache for prefix-based queries

In-Memory Cache

The resolution pipeline can cache resolved values in memory. Cache behaviour is controlled via environment variables:

SPHEREON_CONFIG_CACHE_ENABLED=true
SPHEREON_CONFIG_CACHE_MAX_ENTRIES=1000
SPHEREON_CONFIG_CACHE_EVICTION_POLICY=LRU # LRU, LFU, or FIFO
SPHEREON_CONFIG_CACHE_SNAPSHOT_ENABLED=true
SPHEREON_CONFIG_CACHE_SNAPSHOT_MAX_ENTRIES=200
SPHEREON_CONFIG_CACHE_SNAPSHOT_TTL=1800 # seconds

Offline Cache (EDK)

Cloud providers can persist configuration locally so the application keeps working during network outages. See Offline Cache for details.

Troubleshooting

Provider Not Registering

  1. Check the module is on the classpath: Verify the dependency is in your build file
  2. Check the enable flag: Ensure config.providers.{id}.enabled isn't set to false
  3. Check required config: Some providers need connection details to self-enable (e.g., secrets.vault.address for Vault)

Wrong Value Being Used

  1. Check provider priority: Higher priority sources override lower ones. Environment variables always win.
  2. Check key normalization: api.baseUrl, api.base.url, and API_BASE_URL all resolve to the same normalized key
  3. Check protection: A FINAL property at APP scope cannot be overridden by a TENANT or PRINCIPAL source

Cloud Provider Failing

  1. Check credentials: Verify connection strings, API keys, or managed identity setup
  2. Check network: Ensure firewall allows connections to the cloud service
  3. Check offline cache: If enabled, cached values are returned during outages