0
# Configuration System
1
2
Ktor's configuration system provides flexible application configuration management through multiple sources including files, environment variables, and programmatic configuration. The system supports hierarchical configuration with type-safe access and validation.
3
4
## ApplicationConfig Interface
5
6
### Core Configuration Interface
7
8
```kotlin { .api }
9
interface ApplicationConfig {
10
fun property(path: String): ApplicationConfigValue
11
fun propertyOrNull(path: String): ApplicationConfigValue?
12
fun config(path: String): ApplicationConfig
13
fun configList(path: String): List<ApplicationConfig>
14
fun keys(): Set<String>
15
fun toMap(): Map<String, Any?>
16
}
17
```
18
19
### Configuration Value Interface
20
21
```kotlin { .api }
22
interface ApplicationConfigValue {
23
val type: Type
24
25
fun getString(): String
26
fun getList(): List<String>
27
fun getInt(): Int
28
fun getLong(): Long
29
fun getFloat(): Float
30
fun getDouble(): Double
31
fun getBoolean(): Boolean
32
}
33
```
34
35
### Basic Configuration Usage
36
37
```kotlin { .api }
38
// Access configuration properties
39
fun Application.loadConfig() {
40
val config = environment.config
41
42
// String properties
43
val appName = config.property("app.name").getString()
44
val version = config.propertyOrNull("app.version")?.getString() ?: "1.0.0"
45
46
// Numeric properties
47
val port = config.property("server.port").getInt()
48
val timeout = config.property("server.timeout").getLong()
49
val rate = config.property("app.rate").getDouble()
50
51
// Boolean properties
52
val debugMode = config.property("app.debug").getBoolean()
53
val enableLogging = config.propertyOrNull("logging.enabled")?.getBoolean() ?: true
54
55
// List properties
56
val allowedHosts = config.property("server.allowedHosts").getList()
57
val modules = config.propertyOrNull("app.modules")?.getList() ?: emptyList()
58
}
59
```
60
61
## Configuration Implementations
62
63
### MapApplicationConfig
64
65
```kotlin { .api }
66
// Configuration backed by Map
67
class MapApplicationConfig(vararg values: Pair<String, String>) : ApplicationConfig {
68
constructor(map: Map<String, String>)
69
70
// Add or update configuration values
71
fun put(key: String, value: String)
72
fun putAll(values: Map<String, String>)
73
}
74
75
// Create programmatic configuration
76
val config = MapApplicationConfig(
77
"app.name" to "MyKtorApp",
78
"server.port" to "8080",
79
"server.host" to "0.0.0.0",
80
"database.url" to "jdbc:h2:mem:test",
81
"database.driver" to "org.h2.Driver",
82
"logging.level" to "INFO"
83
)
84
85
// Use with application environment
86
val environment = applicationEnvironment {
87
this.config = config
88
}
89
```
90
91
### MergedApplicationConfig
92
93
```kotlin { .api }
94
// Configuration that merges multiple configs with precedence
95
class MergedApplicationConfig(
96
vararg configs: ApplicationConfig
97
) : ApplicationConfig {
98
// Later configs override earlier ones
99
}
100
101
// Example: Environment variables override file config
102
val fileConfig = HoconApplicationConfig(ConfigFactory.load())
103
val envConfig = MapApplicationConfig(System.getenv().mapKeys { "env.${it.key}" })
104
val merged = MergedApplicationConfig(fileConfig, envConfig)
105
```
106
107
## HOCON Configuration Support
108
109
### HoconApplicationConfig
110
111
```kotlin { .api }
112
// Configuration using Typesafe Config (HOCON format)
113
class HoconApplicationConfig(
114
private val config: Config
115
) : ApplicationConfig {
116
117
constructor(configPath: String) : this(ConfigFactory.load(configPath))
118
119
// Access nested configuration
120
override fun config(path: String): ApplicationConfig {
121
return HoconApplicationConfig(config.getConfig(path))
122
}
123
}
124
125
// Load HOCON configuration
126
val config = HoconApplicationConfig(ConfigFactory.load())
127
128
// application.conf example:
129
/*
130
app {
131
name = "MyKtorApp"
132
version = "1.0.0"
133
debug = false
134
135
server {
136
port = 8080
137
host = "0.0.0.0"
138
ssl {
139
enabled = false
140
keyStore = "keystore.jks"
141
keyAlias = "mykey"
142
}
143
}
144
145
database {
146
url = "jdbc:postgresql://localhost:5432/mydb"
147
driver = "org.postgresql.Driver"
148
user = "dbuser"
149
password = "dbpass"
150
151
pool {
152
maxSize = 20
153
minIdle = 5
154
maxLifetime = 1800000
155
}
156
}
157
158
logging {
159
level = "INFO"
160
appenders = ["console", "file"]
161
162
file {
163
path = "logs/app.log"
164
maxSize = "10MB"
165
maxHistory = 30
166
}
167
}
168
}
169
*/
170
```
171
172
### Hierarchical Configuration Access
173
174
```kotlin { .api }
175
fun Application.configureFromHocon() {
176
val config = environment.config
177
178
// Access nested configurations
179
val serverConfig = config.config("server")
180
val port = serverConfig.property("port").getInt()
181
val host = serverConfig.property("host").getString()
182
183
val sslConfig = serverConfig.config("ssl")
184
val sslEnabled = sslConfig.property("enabled").getBoolean()
185
186
if (sslEnabled) {
187
val keyStore = sslConfig.property("keyStore").getString()
188
val keyAlias = sslConfig.property("keyAlias").getString()
189
// Configure SSL
190
}
191
192
// Access configuration lists
193
val dbConfigs = config.configList("databases")
194
dbConfigs.forEach { dbConfig ->
195
val url = dbConfig.property("url").getString()
196
val driver = dbConfig.property("driver").getString()
197
// Configure database connection
198
}
199
}
200
```
201
202
## Environment Variable Configuration
203
204
### Environment Integration
205
206
```kotlin { .api }
207
// Create configuration from environment variables
208
fun createEnvironmentConfig(): ApplicationConfig {
209
val envVars = System.getenv()
210
val configMap = mutableMapOf<String, String>()
211
212
// Map environment variables to config paths
213
envVars.forEach { (key, value) ->
214
when {
215
key.startsWith("KTOR_") -> {
216
val configKey = key.removePrefix("KTOR_")
217
.lowercase()
218
.replace('_', '.')
219
configMap[configKey] = value
220
}
221
}
222
}
223
224
return MapApplicationConfig(configMap)
225
}
226
227
// Example environment variables:
228
// KTOR_SERVER_PORT=8080
229
// KTOR_SERVER_HOST=0.0.0.0
230
// KTOR_DATABASE_URL=jdbc:postgresql://localhost/mydb
231
// KTOR_LOGGING_LEVEL=DEBUG
232
233
// Usage
234
val envConfig = createEnvironmentConfig()
235
val fileConfig = HoconApplicationConfig(ConfigFactory.load())
236
val config = MergedApplicationConfig(fileConfig, envConfig) // Env overrides file
237
```
238
239
### System Properties Integration
240
241
```kotlin { .api }
242
// Create configuration from system properties
243
fun createSystemPropsConfig(): ApplicationConfig {
244
val sysProps = System.getProperties()
245
val configMap = mutableMapOf<String, String>()
246
247
sysProps.forEach { key, value ->
248
if (key.toString().startsWith("ktor.")) {
249
configMap[key.toString()] = value.toString()
250
}
251
}
252
253
return MapApplicationConfig(configMap)
254
}
255
256
// Usage with JVM arguments:
257
// java -Dktor.server.port=8080 -Dktor.debug=true MyApp
258
```
259
260
## Configuration Loaders
261
262
### ConfigLoaders Object
263
264
```kotlin { .api }
265
object ConfigLoaders {
266
fun file(path: String): ApplicationConfig {
267
return HoconApplicationConfig(ConfigFactory.parseFile(File(path)))
268
}
269
270
fun classpath(resource: String): ApplicationConfig {
271
return HoconApplicationConfig(ConfigFactory.parseResources(resource))
272
}
273
274
fun properties(path: String): ApplicationConfig {
275
val props = Properties()
276
File(path).inputStream().use { props.load(it) }
277
return MapApplicationConfig(props.toMap() as Map<String, String>)
278
}
279
280
fun environment(): ApplicationConfig {
281
return createEnvironmentConfig()
282
}
283
284
fun systemProperties(): ApplicationConfig {
285
return createSystemPropsConfig()
286
}
287
}
288
289
// Load configuration from multiple sources
290
val config = MergedApplicationConfig(
291
ConfigLoaders.classpath("application.conf"), // Default config
292
ConfigLoaders.file("config/production.conf"), // Environment-specific
293
ConfigLoaders.environment(), // Environment variables
294
ConfigLoaders.systemProperties() // JVM system properties
295
)
296
```
297
298
## Configuration Decoder
299
300
### MapConfigDecoder
301
302
```kotlin { .api }
303
// Decoder for map-based configurations
304
class MapConfigDecoder {
305
fun decode(map: Map<String, Any?>): ApplicationConfig {
306
return MapApplicationConfig().apply {
307
map.forEach { (key, value) ->
308
when (value) {
309
is String -> put(key, value)
310
is Number -> put(key, value.toString())
311
is Boolean -> put(key, value.toString())
312
is List<*> -> {
313
// Handle list values
314
value.forEachIndexed { index, item ->
315
put("$key.$index", item.toString())
316
}
317
}
318
is Map<*, *> -> {
319
// Handle nested maps
320
@Suppress("UNCHECKED_CAST")
321
val nested = value as Map<String, Any?>
322
nested.forEach { (nestedKey, nestedValue) ->
323
put("$key.$nestedKey", nestedValue.toString())
324
}
325
}
326
}
327
}
328
}
329
}
330
}
331
```
332
333
## Deployment Configuration Extensions
334
335
### Standard Deployment Properties
336
337
```kotlin { .api }
338
// Extension properties for common deployment settings
339
val ApplicationConfig.port: Int
340
get() = propertyOrNull("ktor.deployment.port")?.getInt() ?: 8080
341
342
val ApplicationConfig.host: String
343
get() = propertyOrNull("ktor.deployment.host")?.getString() ?: "0.0.0.0"
344
345
val ApplicationConfig.sslPort: Int?
346
get() = propertyOrNull("ktor.security.ssl.port")?.getInt()
347
348
val ApplicationConfig.developmentMode: Boolean
349
get() = propertyOrNull("ktor.development")?.getBoolean() ?: false
350
351
val ApplicationConfig.rootPath: String
352
get() = propertyOrNull("ktor.deployment.rootPath")?.getString() ?: ""
353
354
// Usage
355
fun Application.configureFromDeployment() {
356
val config = environment.config
357
358
log.info("Starting server on ${config.host}:${config.port}")
359
log.info("Development mode: ${config.developmentMode}")
360
log.info("Root path: ${config.rootPath}")
361
362
config.sslPort?.let { sslPort ->
363
log.info("SSL enabled on port $sslPort")
364
}
365
}
366
```
367
368
### Watch Paths Configuration
369
370
```kotlin { .api }
371
// Configure watch paths for development auto-reload
372
fun ApplicationEnvironmentBuilder.configureWatchPaths(config: ApplicationConfig) {
373
config.propertyOrNull("ktor.deployment.watch")?.getList()?.forEach { path ->
374
watchPaths.add(path)
375
}
376
377
// Or from individual watch entries
378
config.configList("ktor.deployment.watch").forEach { watchConfig ->
379
val path = watchConfig.property("path").getString()
380
watchPaths.add(path)
381
}
382
}
383
384
// application.conf example:
385
/*
386
ktor {
387
deployment {
388
watch = [
389
"src/main/kotlin",
390
"src/main/resources"
391
]
392
393
# Alternative format
394
watch = [
395
{ path = "src/main/kotlin" },
396
{ path = "src/main/resources" }
397
]
398
}
399
}
400
*/
401
```
402
403
## Type-Safe Configuration
404
405
### Configuration Data Classes
406
407
```kotlin { .api }
408
// Define configuration data classes
409
data class ServerConfig(
410
val port: Int,
411
val host: String,
412
val ssl: SslConfig?
413
)
414
415
data class SslConfig(
416
val port: Int,
417
val keyStore: String,
418
val keyPassword: String,
419
val keyAlias: String
420
)
421
422
data class DatabaseConfig(
423
val url: String,
424
val driver: String,
425
val user: String,
426
val password: String,
427
val pool: PoolConfig
428
)
429
430
data class PoolConfig(
431
val maxSize: Int,
432
val minIdle: Int,
433
val maxLifetime: Long
434
)
435
436
// Extension functions for type-safe access
437
fun ApplicationConfig.getServerConfig(): ServerConfig {
438
val serverConfig = config("server")
439
return ServerConfig(
440
port = serverConfig.property("port").getInt(),
441
host = serverConfig.property("host").getString(),
442
ssl = serverConfig.propertyOrNull("ssl")?.let {
443
val sslConfig = serverConfig.config("ssl")
444
SslConfig(
445
port = sslConfig.property("port").getInt(),
446
keyStore = sslConfig.property("keyStore").getString(),
447
keyPassword = sslConfig.property("keyPassword").getString(),
448
keyAlias = sslConfig.property("keyAlias").getString()
449
)
450
}
451
)
452
}
453
454
fun ApplicationConfig.getDatabaseConfig(): DatabaseConfig {
455
val dbConfig = config("database")
456
return DatabaseConfig(
457
url = dbConfig.property("url").getString(),
458
driver = dbConfig.property("driver").getString(),
459
user = dbConfig.property("user").getString(),
460
password = dbConfig.property("password").getString(),
461
pool = dbConfig.config("pool").let { poolConfig ->
462
PoolConfig(
463
maxSize = poolConfig.property("maxSize").getInt(),
464
minIdle = poolConfig.property("minIdle").getInt(),
465
maxLifetime = poolConfig.property("maxLifetime").getLong()
466
)
467
}
468
)
469
}
470
```
471
472
## Configuration Validation
473
474
### Configuration Validation Utilities
475
476
```kotlin { .api }
477
// Validation extension functions
478
fun ApplicationConfigValue.requireInt(min: Int? = null, max: Int? = null): Int {
479
val value = getInt()
480
min?.let { require(value >= it) { "Value must be >= $it, got $value" } }
481
max?.let { require(value <= it) { "Value must be <= $it, got $value" } }
482
return value
483
}
484
485
fun ApplicationConfigValue.requireString(pattern: Regex? = null): String {
486
val value = getString()
487
require(value.isNotBlank()) { "Value cannot be blank" }
488
pattern?.let { require(value.matches(it)) { "Value '$value' doesn't match pattern $it" } }
489
return value
490
}
491
492
fun ApplicationConfig.requireProperty(path: String): ApplicationConfigValue {
493
return propertyOrNull(path) ?: throw IllegalStateException("Required property '$path' not found")
494
}
495
496
// Validation example
497
fun Application.validateConfiguration() {
498
val config = environment.config
499
500
try {
501
// Validate required properties
502
val port = config.requireProperty("server.port").requireInt(min = 1, max = 65535)
503
val host = config.requireProperty("server.host").requireString()
504
val dbUrl = config.requireProperty("database.url")
505
.requireString(Regex("^jdbc:[a-zA-Z0-9]+://.*"))
506
507
log.info("Configuration validated successfully")
508
log.info("Server will start on $host:$port")
509
510
} catch (e: IllegalStateException) {
511
log.error("Configuration validation failed: ${e.message}")
512
throw e
513
} catch (e: IllegalArgumentException) {
514
log.error("Configuration validation failed: ${e.message}")
515
throw e
516
}
517
}
518
```
519
520
## Complete Configuration Example
521
522
### Multi-Environment Configuration
523
524
```kotlin { .api }
525
// Environment-specific configuration loading
526
class ConfigurationManager {
527
528
fun loadConfiguration(environment: String = "development"): ApplicationConfig {
529
val configs = mutableListOf<ApplicationConfig>()
530
531
// Base configuration
532
configs.add(ConfigLoaders.classpath("application.conf"))
533
534
// Environment-specific configuration
535
val envConfigFile = "application-$environment.conf"
536
try {
537
configs.add(ConfigLoaders.classpath(envConfigFile))
538
} catch (e: Exception) {
539
println("Environment config $envConfigFile not found, using defaults")
540
}
541
542
// External configuration file (if exists)
543
val externalConfig = File("config/application.conf")
544
if (externalConfig.exists()) {
545
configs.add(ConfigLoaders.file(externalConfig.path))
546
}
547
548
// Environment variables (highest priority)
549
configs.add(ConfigLoaders.environment())
550
551
// System properties (highest priority)
552
configs.add(ConfigLoaders.systemProperties())
553
554
return MergedApplicationConfig(*configs.toTypedArray())
555
}
556
}
557
558
// Application configuration setup
559
fun main() {
560
val environment = System.getenv("APP_ENV") ?: "development"
561
val configManager = ConfigurationManager()
562
val config = configManager.loadConfiguration(environment)
563
564
val app = embeddedServer(Netty, environment = applicationEnvironment {
565
this.config = config
566
configure(config)
567
developmentMode = config.developmentMode
568
}) {
569
configureApplication()
570
}
571
572
app.start(wait = true)
573
}
574
575
fun Application.configureApplication() {
576
val config = environment.config
577
578
// Validate configuration
579
validateConfiguration()
580
581
// Load type-safe configuration
582
val serverConfig = config.getServerConfig()
583
val dbConfig = config.getDatabaseConfig()
584
585
// Configure application based on config
586
configureDatabase(dbConfig)
587
configureSecurity(config)
588
configureRouting()
589
590
log.info("Application configured successfully")
591
}
592
593
fun Application.configureDatabase(dbConfig: DatabaseConfig) {
594
// Database configuration based on config
595
log.info("Configuring database: ${dbConfig.url}")
596
}
597
598
fun Application.configureSecurity(config: ApplicationConfig) {
599
val securityConfig = config.config("security")
600
601
if (securityConfig.propertyOrNull("jwt.enabled")?.getBoolean() == true) {
602
val jwtSecret = securityConfig.property("jwt.secret").getString()
603
val jwtIssuer = securityConfig.property("jwt.issuer").getString()
604
// Configure JWT
605
log.info("JWT security configured")
606
}
607
608
if (securityConfig.propertyOrNull("cors.enabled")?.getBoolean() == true) {
609
val allowedHosts = securityConfig.property("cors.allowedHosts").getList()
610
// Configure CORS
611
log.info("CORS configured for hosts: $allowedHosts")
612
}
613
}
614
615
// Configuration files structure:
616
/*
617
resources/
618
├── application.conf # Base configuration
619
├── application-development.conf # Development overrides
620
├── application-staging.conf # Staging overrides
621
├── application-production.conf # Production overrides
622
└── logback.xml # Logging configuration
623
624
config/ # External configuration
625
└── application.conf # Local overrides
626
*/
627
```
628
629
This comprehensive configuration documentation covers all aspects of Ktor's configuration system, from basic property access to advanced multi-environment configuration management with validation and type safety.