Photo by Derek Story on Unsplash

A Suggestion on Managing Profiles in Spring Boot

Mohamed Al Sayadi
Level Up Coding
Published in
5 min readJan 22, 2020

--

Introduction

Spring Boot provides out-of-the-box support for managing different environments through what Spring calls profiles; dev, staging, test, prod, etc. Normally, each profile would load shared variables and configurations from the shared application.properties file then from an environment-specific file, application-dev.properties for example.

The Issue

More often than not, environments that are not production. dev, staging, test, share configurations that only differ with production for something such as logging level; usually, you’d set logging to debug on dev, staging, and test but limit it to info on production. This might leave us with repeated (think copy and paste) configurations across multiple files, and repeated code is usually a sign for potential improvement.

What We Can Do

Given how environment configuration is loaded within Spring Boot, one might be tempted to keep such configurations in the shared application.properties and then overwrite them in the production environment, application-prod.properties. This does solve the problem of repetition, but it leaves the ‘shared’ application.properties in a weird state where it has all the shared configurations but not really; some of these configurations will be overwritten. More importantly, this approach risks “leaking” these configurations accidentally into production.

Photo on Foter.com

The Suggestion

While the previous solution works and one might think is encouraged (otherwise, why do Spring profiles work this way then), a cleaner approach would be to introduce a non-prod profile that gets loaded into each non-production environment. This approach achieves the following:

1- Keeps the application.properties file truly for shared properties across all environments.

2- Explicitly shows configurations that are meant for non-production development or testing purposes.

3- Minimizes the number of repeated configurations.
This benefit is, especially, apparent further down the deployment pipeline when environment variables need to be provided for say Docker orchestrators such as Kubernetes, Elastic Beanstalk, etc…

The Details

The specifics of how to, exactly, accomplish this suggestion for dev, staging and prod profiles can be summarized as:

1- Create the following properties files:
* application.properties
* application-non-prod.properties
* application-dev.properties
* application-staging.properties
* application-prod.properties

2- To the non-production profiles, namelyapplication-dev.properties, and application-staging.properties add this line: spring.profiles.include=non-prod. This tells Spring to include the non-prod “profile” to the list of active profiles which makes all the properties in application-non-prod.properties available to the application.

3- Follow these conventions:
* If a property will retain its value across all profiles → add it to application.properties.
* If a property should only have a different value for production → add it to bothapplication-non-prod.propertie and application-prod.properties.
* If a property changes values per profile → add it to all profiles; applicatio-dev.properties, application-staging.properties and application-prod.properties.

Photo on Foter.com

An Example Implementation

To create a minimal application to test this setup, we can start with an empty Spring Boot app with at least thespring-boot-web-starter dependency and add the following to our profile files:

Sample 1: application.properties

prop1=This is a shared value across all profiles for prop1.

Sample 2: application-non-prod.properties

prop2=Non-production-specific value for prop2.

Sample 3: application-dev.properties

spring.profiles.include=non-prod

prop3=Development-specific value for prop3.

Sample 4: application-staging.properties

spring.profiles.include=non-prod

prop3=Staging-specific value for prop3.

Sample 5: application-prod.properties

prop2=Production-specific value for prop2.

prop3=Production-specific value for prop3.

Once these files are all set up, add an endpoint /properties that accepts a key and returns its value from the environment for easy testing:

Sample 6: PropertyController.java

@RestController
@RequestMapping("/properties")
public class PropertyController {

private final Environment environment;

@Autowired
public PropertyController(
Environment environment) {

this.environment = environment;
}

@GetMapping
public String getProperty(@NotNull String key) {

return environment.getProperty(key);
}
}

Testing the dev Profile

Starting the application in dev mode

To start the application with the dev profile set as active, we can use the command:

mvn spring-boot:run -Dspring-boot.run.profiles=dev

Verifying Spring’s Logs

Once the command is run, Spring should display both dev (the profile set by us) and non-prod (the profile “included” with dev) as active profiles:

.s.s.SpringProfilesSuggestionApplication : The following profiles are active: dev,non-prod

Assuming the application is running locally on the default 8080 port, we can verify that properties were loaded as expected.

Verifying prop1 value

Issuing the curl command

curl localhost:8080/properties?key=prop1

should return:

This is a shared value across all profiles for prop1.

which verifies that shared properties from application.propertieswere loaded properly.

Verifying prop2 value

And issuing the command:

curl localhost:8080/properties?key=prop2

should return:

Non-production-specific value for prop2

which verifies that non-production properties from application-non-prod.properties were loaded properly and that’s the whole point on this setup.

Verifying prop3 value

While issuing the command:

curl localhost:8080/properties?key=prop3

should return:

Development-specific value for prop3

which verifies that specific-profile properties from application-dev.properties were loaded properly.

Testing the prod Profile

Testing the prod profile follows the exact same path as the dev profile. This section might get monotonic but I want to lay it down for completeness.

Starting the application in prod mode

To start the application with the prod profile:

mvn spring-boot:run -Dspring-boot.run.profiles=prod

Verifying Spring’s Logs

Spring should display the log:

.s.s.SpringProfilesSuggestionApplication : The following profiles are active: prod

Verifying prop1 value

Issuing the command:

curl localhost:8080/properties?key=prop1

should return:

This is a shared value across all profiles for prop1

Verifying prop2 value

While issuing the command:

curl localhost:8080/properties?key=prop2

should return:

Production-specific value for prop2

Verifying prop3 value

And issuing the command:

curl localhost:8080/properties?key=prop3

should, also, return:

Production-specific value for prop3
Photo on Foter.com

The Code

If you’d like a test drive with minimal effort, I’ve put together the same setup handy on GitHub: https://github.com/sayadi/spring-profiles-suggestion.

--

--