5 Observations on Spring Boot's Loading Precedence for Properties Files With Spring Cloud Config

Medium Link: 5 Observations on Spring Boot’s Loading Precedence for Properties Files With Spring Cloud Config
If you have a medium.com membership, I would appreciate it if you read this article on medium.com instead to support me~ Thank You! 🚀
images/unsplash.jpg
Photo by Brett Jordan on Unsplash

Intro

Do you externalize and centralize your configurations using Spring Cloud Config Server? Do you use Spring Profiles to manage your configurations for different deployment environments? Do you get confused about which configurations in your Spring Boot applications take the higher priority when you deploy them? Me too!

Join me in this article where I share my experience with using Spring Cloud Config in Spring Boot applications and the order of precedence for properties files when running the Spring Boot applications.

Note: This article will not go into detail on how to implement Spring Cloud Config Server and will require some knowledge in deploying Spring Boot applications. At the point of writing, Spring Boot Version = 2.7.0, Spring Cloud Version = 2021.0.3-SNAPSHOT, and Spring Cloud Config Version = 3.1.3-SNAPSHOT.


Background Context

Before we start, let’s align our knowledge on the concepts of configurations in Spring Boot applications as well as how Spring Cloud Config and Spring Profiles come into play.

images/containerization.png
Process for Containerizing and Deploying Spring Boot Microservices

The diagram above is a quick overview of how we can containerize a Spring Boot application and deploy it as a microservice. As you have noticed, when deploying the Spring Boot application, you can pass in properties to alter the behavior of the application for different deployment environments.

These properties are typically defined in properties files (application.yaml or application.properties) and they can be overridden by environment variables. An example would be connecting to different database instances in the development vs production environment as shown in the diagram below.

images/deployment-env.png
Customizing database URL based on the deployment environment

However, it can be tedious to manage the properties for different deployment environments. Hence, we use Spring Profiles to aid us.

Integrating with Spring Profiles

Spring Profiles allows us to define properties files categorized by “profiles”. When you are deploying your Spring Boot application, you will define the active Spring Profiles using environment variables to load the profile-specific properties (AKA deployment environment-specific properties). Here’s an example using the docker command.

docker run --env SPRING_PROFILES_ACTIVE='dev' spring-boot-app

Below is a simple illustration of how properties are loaded using Spring Profiles:

images/spring-profiles.png
Loading properties using Spring Profiles

Additionally, Spring Profiles allows us to load multiple properties files. If you were to set your profiles as follows:

  • spring.profiles.active="dev" → This will load application.yaml and application-dev.yaml properties files.
  • spring.profiles.active="dev, local" → This will load application.yaml, application-dev.yaml and application-local.yaml properties files.

Externalized Configurations with Spring Cloud Config

Internal Configurations is where you store the properties files within the Spring Boot applications in the classpath. These properties files are packaged within the application, hence we can also call them packaged files.

classpath refers to the location where resource files are stored and in a Maven project, the standard location for storing the application’s resources is in the src/main/resources folder.

An issue with internal configurations is that every time a change to the configuration is required, you will have to re-build the container and re-deploy the application as shown below.

images/internal-config.png
Internal Configurations Approach

This issue can be simplified using externalized configurations where we override the properties using Java System Properties, Environment variables, Command-Line arguments, or external properties files.

Spring Cloud Config is an approach that allows you to store your properties files externally and retrieves them when the Spring Boot application starts. Hence, we will only need to re-deploy the application to reflect the configuration changes as shown below.

images/external-config.png
External Configurations Approach using Spring Cloud Config

With these, we have covered the basic background context on Spring Boot properties, Spring Profiles, and Spring Cloud Config, let’s dive deeper into understanding the order of precedence for properties files that are loaded from different sources.

How do I configure my Spring Cloud Config Server?

I am using Git to store my configuration data. The diagram below is the folder structure for my configuration data where {application} is a placeholder that refers to the Spring Boot application’s name configurable by the property spring.application.name.

images/config-repo.png
Folder structure for my configuration data in the remote repository

The Spring Cloud Config Server is configured to connect to a single repository with the configuration data stored in the config-repo folder. Additionally, there are 2 search paths (subfolders) within the config-repo folder.

images/config-server-git.png
Spring Cloud Config Server Configurations (partial)

The shared subfolder is where all the common configurations shared across all Spring Boot applications are stored and each Spring Boot application has its own {application} subfolder.

Looking into the order of precedence

Let’s get started! You can refer to the order of precedence for externalized configuration in Spring Boot’s documentation. Here, I will try to demonstrate the common scenarios for configuring externalized configurations in Spring Boot.

To frame the context for the demonstration, I will have a Spring Boot application (config-client) with 2 Spring Profiles (dev, local), a Spring Cloud Config Server, and several properties files as defined below.

images/demo.png
Overview of Demonstration Setup

There are 3 main locations for the properties files that I have configured.

  • The classpath root - src/main/resources (Internal)
  • Spring Cloud Config Server (External)
  • A /config folder (External)

The Spring Boot application will load the properties from these locations and add them to the Spring Environment.

Normal Deployments with Spring Profiles

Using a deployment scenario for the demonstration setup, let’s deploy the Spring Boot application with the Spring Profiles dev and local using the command below.

docker run \ 
   --env SPRING_PROFILES_ACTIVE='dev, local' \
   --env SPRING_CONFIG_ADDITIONAL_LOCATION='/config/' \
   --volume <path_to_config_folder>:/config \
   config-client

When the Spring Boot application starts, it will load the properties files in the following order of precedence (highest to lowest):

  • Config Dir: config/application-local.yaml (profile-specific)
  • Config Dir: config/application-dev.yaml (profile-specific)
  • Config Dir: config/application.yaml (default)
  • Config Server: config-client/config-client-local.yaml (profile-specific)
  • Config Server: shared/application-local.yaml (shared, profile-specific)
  • Config Server: config-client/config-client-dev.yaml (profile-specific)
  • Config Server: shared/application-dev.yaml (shared, profile-specific)
  • Classpath: resources/application-local.yaml (profile-specific)
  • Classpath: resources/application-dev.yaml (profile-specific)
  • Config Server: config-client/config-client.yaml (default)
  • Config Server: shared/application.yaml (shared, default)
  • Classpath: resources/application.yaml (default)

What are the key observations about the order of precedence?

Based on the preceding example and my findings from the documentation, I have identified 5 key observations.

1 - Properties vs Yaml files

.properties files have a higher priority than .yaml files as mentioned in the Spring’s documentation. It is also highly recommended to stick to a single file format.

2 - Spring Profiles

Profile-specific properties files always override non-specific properties files. Eg. application-{profile}.yaml > application.yaml.

In the event of multiple Spring Profiles, a last-win strategy is applied where the order of precedence for properties files is from the right to the left. Eg. application-local.yaml > application-dev.yaml for Spring Profiles spring.profiles.active="dev, local".

3 - Config Server Search Paths Configurations

The configuration for search paths in the Spring Cloud Config Server (spring.cloud.config.server.git.search-paths) applies a last-win strategy. This means that the properties in the latter folders will have a higher priority. Using my configuration above, the priorities for the properties files will be as such config-repo/{application} > config-repo/shared.

4 - How Config-Client loads Properties from Spring Cloud Config Server

With reference to Spring Boot 2.4 onwards, the Spring Cloud Config Client documentation recommends configurations from the Spring Cloud Config Server to be imported via the spring.config.import property. If we were to take a look at Spring Boot’s blog update on config file processing in Spring 2.4, you will find the following statement:

Imports can be considered as additional documents inserted just below the document that declares them. They follow the same top-down ordering as regular multi-document files: An import will only be imported once, no matter how many times it is declared.

This means that properties files from Spring Cloud Config Server are resolved and prioritized against properties files in the same location group as the “document” that declares them. Moreover, properties files from Spring Cloud Config Server will have a higher priority than the “document” due to the way it is imported.

In our example above, we declared the property spring.config.import: <Config Server URL> in the Spring Boot application’s classpath (resources/application.yaml). Hence, all properties files from the Spring Cloud Config Server and classpath will be prioritized and resolved under the same location group in the following order (highest to lowest):

  • profile-specific properties files (config-server)
  • profile-specific properties files (classpath)
  • default properties files (config-server)
  • default properties files (classpath)

However, strangely enough, ALL profile-specific properties files (in config-server) that are imported via spring.config.import has a higher priority compared to the profile-specific properties files in the classpath. I believe this is the intended behavior for externally imported files.

5 - Externalized Properties Files

Referencing the Spring Boot’s Config Data Migration Guide, external files always override packaged files (profile-specific or not). Hence, you will notice the new change where the property spring.config.location defines the following default location groups with the latter one having a higher priority.

optional:classpath:/;optional:classpath:/config/
optional:file:./;optional:file:./config/;optional:file:./config/*/

In our example above, all properties files in the /config external folder have a higher priority compared to the Spring Cloud Config Server or classpath. The properties files are prioritized and resolved within each location group in the following order (highest to lowest)

  • /config folder (External) → spring.config.additional.location
  • Spring Cloud Config Server (External) > classpath (Internal) → same location group as explained in point 4 above

On a side note, if the spring.config.import property for Spring Cloud Config Server was defined in the external folder /config/application.yaml, then the order of precedence would change since /config and Spring Cloud Config Server will be in the same location group.

Summary

That’s it! These are the 5 key observations that I have identified as I am working with Spring Boot and Spring Cloud Config. Depending on the way you bind the Spring Cloud Config Server to the Spring Config Client, the order of precedence may change, but the concepts should remain the same.

Do note that I am using a very fundamental setup with multiple profile-specific properties files. There are also many other approaches that you can use such as multi-document properties files, profile groups, profile activations, or Kubernetes Config Maps which were introduced in Spring Boot 2.4.

As for the Spring Cloud Config Server, you can also configure it to load from multiple git repositories or Vault. Do check all these cool features out on your own.

Lastly, I don’t think this article is anything new for people who are experienced with Spring Boot and Spring Cloud Config, but I do hope that it will be useful for people who are new to Spring Cloud Config :)

Refer to the Git Repo below for the code reference.


Thank you for reading till the end! ☕
If you enjoyed this article and would like to support my work, feel free to buy me a coffee on Ko-fi. Your support helps me keep creating, and I truly appreciate it! 🙏