Using Configuration Trees

When running applications on a cloud platform (such as Kubernetes) you often need to read config values that the platform supplies. It is not uncommon to use environment variables for such purposes, but this can have drawbacks, especially if the value is supposed to be kept secret.

As an alternative to environment variables, many cloud platforms now allow you to map configuration into mounted data volumes. For example, Kubernetes can volume mount both ConfigMaps and Secrets.

There are two common volume mount patterns that can be used:

  1. A single file contains a complete set of properties (usually written as YAML).

  2. Multiple files are written to a directory tree, with the filename becoming the ‘key’ and the contents becoming the ‘value’.

For the first case, you can import the YAML or Properties file directly using spring.config.import as described above. For the second case, you need to use the configtree: prefix so that Spring Boot knows it needs to expose all the files as properties.

As an example, let’s imagine that Kubernetes has mounted the following volume:

etc/
  config/
    myapp/
      username
      password

The contents of the username file would be a config value, and the contents of password would be a secret.

To import these properties, you can add the following to your application.properties or application.yaml file:

  • Properties

  • YAML

spring.config.import=optional:configtree:/etc/config/
spring:
  config:
    import: "optional:configtree:/etc/config/"

You can then access or inject myapp.username and myapp.password properties from the Environment in the usual way.

The folders under the config tree form the property name. In the above example, to access the properties as username and password, you can set spring.config.import to optional:configtree:/etc/config/myapp.
Filenames with dot notation are also correctly mapped. For example, in the above example, a file named myapp.username in /etc/config would result in a myapp.username property in the Environment.
Configuration tree values can be bound to both string String and byte[] types depending on the contents expected.

If you have multiple config trees to import from the same parent folder you can use a wildcard shortcut. Any configtree: location that ends with /*/ will import all immediate children as config trees.

For example, given the following volume:

etc/
  config/
    dbconfig/
      db/
        username
        password
    mqconfig/
      mq/
        username
        password

You can use configtree:/etc/config/*/ as the import location:

  • Properties

  • YAML

spring.config.import=optional:configtree:/etc/config/*/
spring:
  config:
    import: "optional:configtree:/etc/config/*/"

This will add db.username, db.password, mq.username and mq.password properties.

Directories loaded using a wildcard are sorted alphabetically. If you need a different order, then you should list each location as a separate import

Configuration trees can also be used for Docker secrets. When a Docker swarm service is granted access to a secret, the secret gets mounted into the container. For example, if a secret named db.password is mounted at location /run/secrets/, you can make db.password available to the Spring environment using the following:

  • Properties

  • YAML

spring.config.import=optional:configtree:/run/secrets/
spring:
  config:
    import: "optional:configtree:/run/secrets/"