# Hoplite: configuration files that become Kotlin data classes

> Hoplite is a Kotlin library that loads YAML, JSON, TOML, Hocon or .properties files into your own data classes, with cascading sources, decoders for third party types, prefix binding for modular applications and detailed failure messages. It is a JVM library with a wide set of optional integration modules.

**sksamuel/hoplite** — A boilerplate-free Kotlin config library for loading configuration files as data classes

- Repository: https://github.com/sksamuel/hoplite
- Stars: 1,044 · Forks: 91
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/sksamuel-hoplite

## The problem it solves: a config file that fails loudly

Hoplite is a Kotlin library for loading configuration files into typesafe classes in a boilerplate-free way. You define your configuration using Kotlin data classes, and at startup Hoplite reads from one or more config files and maps the values into those classes. The property that makes it worth a dependency is stated in the same sentence: any missing values, or values that cannot be converted into the required type, will cause the config to fail with detailed error messages. That is the difference from the usual approach, where a missing key surfaces as a null, a default that was never what you meant, or a NumberFormatException three hours into a production incident. The comparison the README draws is with dictionary-based configuration, which cannot tell you at startup that the string in a YAML file is not a valid integer for that field. The audience is a Kotlin or JVM team with a service that has more than a handful of settings, and the library's scope is deliberately the loading and validation of that file, not the management of secrets or the distribution of configuration.

## Three lines of wiring, and the data classes that drive it

The integration is short because the types do the work. You add the library to your build, which for a Gradle build in Groovy is:

```groovy
implementation 'com.sksamuel.hoplite:hoplite-core:<version>'
```

You also need a module for the format or formats you use, which is a separate artifact. Then you define data classes, one top level class such as Config or ProjectNameConfig with a field for each config value, and nested data classes for grouping related settings. The example in the README is a project that needs database config, config for an embedded HTTP server, and a field naming the environment:

```kotlin
data class Database(val host: String, val port: Int, val user: String, val pass: String)
data class Server(val port: Int, val redirectUrl: String)
data class Config(val env: String, val database: Database, val server: Server)
```

The matching YAML file is a plain nested document, and the file name is your own convention, with the README using application-staging.yaml for a staging environment and noting that the name does not matter:

```yaml
env: staging

database:
  host: staging.wibble.com
  port: 3306
  user: theboss
  pass: 0123abcd

server:
  port: 8080
  redirectUrl: /404.html
```

## Loading it, and the two ways to fail

Building the loader is a builder chain, and the entry point is the ConfigLoader class. You create an instance through ConfigLoaderBuilder, add your sources, and call build. The example loads a resource from the classpath:

```kotlin
val config = ConfigLoaderBuilder.default()
               .addResourceSource("/application-staging.yml")
               .build()
               .loadConfigOrThrow<Config>()
```

The default method on the builder sets up recommended defaults, and if you want to start from nothing you use ConfigLoaderBuilder.empty() instead. There are two ways to get your data class back. loadConfigOrThrow<T> throws an exception if the config could not be resolved, and loadConfig<T> returns a ConfigResult validation monad if you want to handle the errors yourself. The README gives a clear recommendation for which to use: for most cases, when you are resolving config at application startup, the exception based approach is better, because you typically want any errors in config to abort application bootstrapping and dump the errors immediately to the console. That is a small design decision with a large effect on how a misconfigured deployment behaves, and it is the reason the library is worth having even if you never use a feature beyond the loader. One wrinkle in the documentation is worth noting for anyone copying the example literally: the file described is named application-staging.yaml while the resource path in the code is /application-staging.yml.

## Cascading sources and the order they are consulted in

Several features build on the same idea of a source, and the most useful is cascading. Config files can be stacked: you start with a default file and layer new configurations on top, and when resolving config, the lookup of a value falls through to the first file that contains a definition. That is the mechanism behind the common pattern of a default config file plus an environment specific one, and it is why a source list is ordered rather than a set. The same source concept covers per-system overrides from JVM system properties, environment variables, JNDI or a per-user local config file, and the format list is wide, covering YAML, JSON, Toml, Hocon and Java .props files, with the explicit note that you can mix and match formats in the same system. That combination is the interesting part for a real deployment. A base file in YAML, an environment file in JSON and a handful of system property overrides is a common shape for containerised JVM services, and Hoplite expresses it as one ordered list of sources rather than a merge function you write yourself.

## Decoders, and what ships in the box

The decoding side is where a configuration library earns its keep, and Hoplite's list is longer than the obvious one. Support covers many standard types, specifically primitives, enums, dates, collection types, inline classes, uuids and nullable types, plus popular Kotlin third party library types such as NonEmptyList, Option and TupleX from Arrow. That last group is a good signal about the intended audience, because Arrow types are not something a general-purpose config library includes by accident; they are there for teams who already use functional Kotlin idioms. For anything not covered out of the box, the Decoder interface is the extension point, described as making it easy to add support for your custom domain types or standard library types. The read side is a closed set of formats, and the write side is yours to define, which is the right way round for a configuration library: the schema belongs to the application, and the file format is a detail of where the file came from.

## Prefix binding, preprocessors and reload

Three features extend the model beyond a single data class. Prefix binding lets you bind selected config to independent data classes, which is described as useful for modular config loading, where independent modules or plugins load their own config from a common set of configuration sources. The example shows two modules in one source and two separate data classes, bound with a ConfigBinder obtained from the loader:

```kotlin
val configBinder = ConfigLoaderBuilder.default()
  .addResourceSource("/application-prod.yml")
  .addResourceSource("/reference.json")
  .build()
  .configBinder()

val module1Config = configBinder.bindOrThrow<Module1Config>("module1")
```

Preprocessors replace placeholders in the file with values resolved from external configuration, with the examples given being AWS Secrets Manager and Azure KeyVault, which is the mechanism for a secret to appear in a config file without being in the file. Reloadable config lets you trigger config reloads on a fixed interval or in response to external events such as consul value changes, and the tree shows dedicated modules for it in hoplite-watch and hoplite-watch-consul. The full module list in the repository shows the range: hoplite-arrow, hoplite-aws, hoplite-aws2, hoplite-aws-kotlin, hoplite-azure, hoplite-consul, hoplite-cronutils, hoplite-datetime, hoplite-gcp, hoplite-hdfs, hoplite-hikaricp, hoplite-hocon, hoplite-javax, hoplite-json, hoplite-toml, hoplite-vault, hoplite-vavr, hoplite-yaml, three micrometer integration modules, and example-maven and example-native directories.

## Where Hoplite is the wrong tool

Four limits are clear. The first is that it solves configuration loading, and nothing adjacent. It does not distribute configuration, it does not manage secrets on its own beyond the preprocessor hook, and it does not provide a schema language that other services can validate against, so a large fleet with per-service contracts still needs a source of truth somewhere else. The second is that the breadth of the module set is a liability in a specific way. Each format and each integration is a separate Maven artifact with its own transitive dependencies, and the AWS, Azure, GCP, Consul, Vault and HDFS modules are the kind of client libraries that pull in large SDK trees, so adding the one preprocessor you need can add a lot of code to your build. The third is the reload feature, which is useful in a controlled deployment but is a sharp edge: a configuration that changes under a running service, with the usual race between the reload and the code reading the old value. The fourth is the version churn. The project is on a 3.0 line with v3.0.0, v3.0.1 and v3.0.3 published within about two weeks in late September and October 2026, so the API is moving, and the changelog is the file to read before pinning. For a service with a dozen settings, none of this matters; for a platform team standardising configuration across many services, it is the difference between a dependency and a platform decision.

## Conclusion

Adopt Hoplite if your JVM or Kotlin service has outgrown a hand-rolled properties loader and you want configuration failures to be legible at startup rather than mysterious later. Do not adopt it if your configuration is small, if you need runtime reloading of arbitrary values, or if you are on a platform where the extra modules drag in heavy dependencies. Before you upgrade, note that the current line is 3.0.x with v3.0.3 published on 2026-10-09, and pick the format module you actually need rather than adding several, since each one is a separate artifact with its own dependencies.

## FAQ

### What is Hoplite?

It is a Kotlin library for loading configuration files into typesafe classes without boilerplate, mapping values from one or more config files into your own data classes. Missing values and values that cannot be converted to the required type cause the load to fail with detailed error messages. It is Apache-2.0 licensed.

### Which config formats does Hoplite support?

YAML, JSON, Toml, Hocon and Java .props files, and the formats can be mixed and matched in the same system, with a separate module to add for each format you use. Overrides can also come from JVM system properties, environment variables, JNDI or a per-user local config file.

### How do I load a config with Hoplite?

Build a loader with ConfigLoaderBuilder.default(), add your sources with addResourceSource and call build, then either loadConfigOrThrow<T>() to abort startup on a bad config or loadConfig<T>() to get a ConfigResult you handle yourself. The README recommends the throwing form at application startup so errors are dumped immediately.

### What is cascading config in Hoplite?

Config files can be stacked, starting with a default file and layering new configurations on top, and the lookup of a value falls through to the first file that contains a definition. That is how a default config plus an environment specific config is expressed, with a per-system override taking precedence.

### How do I add support for my own types in Hoplite?

You implement the Decoder interface, which the documentation describes as making it easy to add support for custom domain types or standard library types not covered out of the box. Standard coverage already includes primitives, enums, dates, collection types, inline classes, uuids and nullable types, plus Arrow's NonEmptyList, Option and TupleX. The current release is v3.0.3 from 2026-10-09.

## Sources

- [Issues](https://github.com/sksamuel/hoplite/issues)
- [License: Apache-2.0](https://github.com/sksamuel/hoplite/blob/master/LICENSE)
- [README](https://github.com/sksamuel/hoplite/blob/master/README.md)
- [Releases](https://github.com/sksamuel/hoplite/releases)
- [sksamuel/hoplite on GitHub](https://github.com/sksamuel/hoplite)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sksamuel-hoplite
