Cloud Configuration Providers
Cloud configuration providers fetch settings from a remote source, enabling you to manage configuration centrally rather than baking it into each application instance. When a value changes, a feature flag, a rate limit, an API endpoint, you update it in one place and all running instances pick up the change on their next refresh cycle.
The EDK ships two cloud providers: Azure App Configuration for Azure-native deployments, and a REST Config Client for connecting to VDX configuration servers or any compatible REST API.
Both providers share the same behavior: they auto-register when their module is on the classpath, refresh periodically (default 30 seconds), persist to the offline cache for disconnected resilience, and support multi-tenant key scoping.
Azure App Configuration
Azure App Configuration is a managed service for centralizing application settings. It's particularly useful when you have multiple services and environments that need to share configuration without duplicating it in each deployment.
The EDK integration adds features on top of the Azure SDK: automatic label-based environment separation, multi-tenant key conventions, sentinel-based change detection, and fallback to the offline cache when Azure is unreachable.
How It Works
Keys in Azure App Configuration are flat strings with optional labels. The EDK uses labels for environment/profile separation (development, staging, production) and key prefixes for tenant scoping:
| Key in Azure | Label | Resolves to |
|---|---|---|
api.base.url | production | APP-scoped api.base.url |
tenant.acme.api.rate-limit | production | TENANT-scoped api.rate-limit for tenant acme |
myapp/feature.new-ui | - | feature.new-ui (with prefix trimming) |
When the provider refreshes, it fetches all keys matching the configured filter and label, strips the key prefix (if configured), and maps them to configuration properties. Tenant-prefixed keys are automatically scoped to the correct tenant in the resolution pipeline.
Configuration
# Connection — use one of these:
azure.appconfig.connection-string=Endpoint=https://myapp.azconfig.io;Id=...;Secret=...
azure.appconfig.endpoint=https://myapp.azconfig.io # with Azure AD auth
# Key filtering
azure.appconfig.key-filter=myapp/* # Only fetch keys matching this pattern
azure.appconfig.key-prefix=myapp/ # Strip this prefix from fetched keys
azure.appconfig.trim-key-prefix=true
# Environment separation
azure.appconfig.label-filter=production # Only fetch keys with this label
# Change detection
azure.appconfig.sentinel-key=app:version # Key to watch for changes
azure.appconfig.refresh-interval=30s
# Tenant scoping
azure.appconfig.tenant-id=my-tenant
Authentication
- Connection String
- Managed Identity
- Service Principal
The simplest approach, a single string contains the endpoint, ID, and secret. Suitable for development and environments where managed identity isn't available.
export AZURE_APPCONFIG_CONNECTION_STRING="Endpoint=https://myapp.azconfig.io;Id=xxxxx;Secret=xxxxx"
The recommended approach for Azure-hosted services. No credentials in configuration, the Azure runtime provides the identity automatically.
export AZURE_APPCONFIG_ENDPOINT="https://myapp.azconfig.io"
# No credentials needed — Azure SDK uses the managed identity automatically
Requires the managed identity to have the App Configuration Data Reader role on the Azure App Configuration instance.
For environments that need explicit credentials (CI/CD, non-Azure hosting).
azure.appconfig.endpoint=https://myapp.azconfig.io
azure.appconfig.auth-method=CLIENT_SECRET
azure.appconfig.tenant-id=your-azure-ad-tenant-id
azure.appconfig.client-id=your-service-principal-id
azure.appconfig.client-secret=${secret:vault:azure/sp-secret}
Change Detection
Azure App Configuration supports two change detection strategies:
Polling: the provider periodically re-fetches all keys at the configured refresh-interval. Simple and reliable, but fetches everything every cycle even if nothing changed.
Sentinel key: configure a sentinel key (e.g., app:version) that you update whenever configuration changes. The provider polls only the sentinel key; if its value changed since the last check, it triggers a full refresh. This reduces API calls significantly in large configurations.
In practice, the sentinel approach works well: your CI/CD pipeline updates the sentinel key after pushing new configuration, and all instances refresh within the next poll interval.
Offline Fallback
If Azure App Configuration becomes unreachable, the provider falls back to the offline cache transparently. The last successful fetch is always persisted to disk, so your application can start and serve requests even during an Azure outage, with stale but functional configuration.
REST Config Client
The REST Config Client fetches configuration from any REST API that implements the VDX settings endpoint format. This is the provider to use when you run your own configuration server, use the VDX platform's configuration management, or need a cloud-agnostic alternative to Azure.
How It Works
The client calls GET /api/v1/config/settings?scope=APP&profile=default&limit=1000 and receives a paginated list of settings. Each setting includes the key, value, type, and optional protection metadata:
{
"items": [
{
"key": "api.base.url",
"value": "https://api.example.com",
"valueType": "STRING",
"isFinal": false,
"isInterpolationProtected": false
}
]
}
The isFinal and isInterpolationProtected fields are carried through the resolution pipeline, enforcing property protection rules from the central configuration server.
Configuration
rest.config.base-url=https://config.example.com
rest.config.tenant-id=my-tenant
rest.config.profile=production
# Authentication
rest.config.auth.method=API_KEY # or BEARER_TOKEN, OAUTH2_CLIENT_CREDENTIALS
rest.config.auth.api-key=my-api-key
rest.config.auth.api-key-header=X-API-Key
# Timeouts
rest.config.connect-timeout-ms=5000
rest.config.read-timeout-ms=30000
rest.config.refresh-interval-ms=30000
# Offline fallback
rest.config.offline-cache-enabled=true
Authentication Options
| Method | Use case |
|---|---|
API_KEY | Simple API key in a custom header. Suitable for internal services. |
BEARER_TOKEN | JWT bearer token. Suitable when tokens are available from another auth flow. |
OAUTH2_CLIENT_CREDENTIALS | Client credentials grant against a token endpoint. Suitable for service-to-service auth. |
NONE | No authentication. Only for development or when auth is handled by the network layer (mTLS, VPN). |
Multi-Platform Support
The REST Config Client is implemented as Kotlin Multiplatform, it runs on JVM, JS (browser and Node.js), and native targets. This means mobile and web applications can fetch centralized configuration from the same server as backend services, using the same provider interface and key conventions.
Health Checks
Both providers expose health check capabilities:
val health = provider.healthCheck().getOrThrow()
// CloudProviderHealth(
// isHealthy = true,
// latencyMs = 45,
// configKeyCount = 127,
// lastSuccessfulRefresh = 2026-03-23T10:30:00Z,
// errorMessage = null
// )
In Spring Boot deployments, health checks can be wired to actuator endpoints to include configuration provider status in readiness probes.
Best Practices
Use labels for environments, not separate App Configuration instances. Azure App Configuration supports label-based filtering. Store all environments in one instance, use labels (development, staging, production) to separate them, and configure azure.appconfig.label-filter per deployment.
Configure a sentinel key for change detection. Polling every 30 seconds with hundreds of keys creates unnecessary load. A sentinel key means the provider only fetches one key per cycle; a full refresh happens only when something actually changed.
Enable offline caching in production. The cost is minimal (a small JSON file on disk) and the benefit is significant, your application survives cloud provider outages gracefully rather than failing to start.
Use property protection for security-critical settings. Mark auth.require-mfa=true as FINAL at APP scope so tenants can't disable it. Mark db.connection-string as INTERPOLATION_PROTECTED so lower scopes can't reference it via ${...} syntax.