31

My intention is to have two profiles in a spring boot application - development and production one. Development profile is meant just to override some variables of production profile (like in-memory database instead of database in the cloud). As I expect some changes to be done to production profile in the future, duplicating variables in development profile doesn't seem to be a solution.

So, in Spring Reference I read that spring.profiles.include is supposed to only add properties from referenced profile.

Sometimes, it is useful to have profile-specific properties that add to the active profiles rather than replace them. The spring.profiles.include property can be used to unconditionally add active profiles.

However, from what I've checked it rather overrides it. So, when having two profiles foo and bar, in separate yaml files:

application-foo.yaml:

myproperty: 44

application-bar.yaml:

spring:
  profiles:
    include: foo
    active: bar,foo
myproperty: 55

And setting -Dspring.profiles.active=bar variable in IDE, the runtime value of myproperty is 44. That means that bar, is overriden with foo which was supposed to only add properties, but not to override them. When starting the application, I get:

The following profiles are active: foo,bar

I added spring.profiles.active=bar to application-bar.yaml as suggested by this answer, in another question, but it has no effect - there is no difference when property is there or not (I also tried using dash listing instead of comma separated values).

My question is, is it how it is supposed to work (then Spring Reference is misleading)? If so, are there any solutions for that?

Adding a link to the application source code on a github.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Daniel Szymatowicz
  • 435
  • 1
  • 4
  • 7

5 Answers5

26

We implemented the Spring active profiles in a slightly different way. Let's say the default properties file, application.yml, contains all default values which is same in both production and development environments.

Create separate properties for production and development files named application-prd.yml and application-dev.yml respectively. These files may contain extra properties or override some of the default properties.

During application startup, we pass the spring.profiles.active as an environment variable. For example,

-Dspring.profiles.active=prd

will pick up application-prd.yml along with application.yml

or

-Dspring.profiles.active=dev

will pick up application-dev.yml along with application.yml

Indra Basak
  • 7,124
  • 1
  • 26
  • 45
  • Thanks, I think its good enough for my purpose. It still doesnt make developer to be able to override custom property files, so that makes me create additional file (eventually there are application.yaml, application-foo.yaml and application-bar.yaml), but as long as I am able to meet the requirement its fine. – Daniel Szymatowicz Nov 19 '17 at 12:48
14

According to the spring boot documentation here, spring.profiles.include is used to add the properties from other profiles. It will add the property from other profiles if the property is not present in active one. But if it is present, then it will overwrite and the last one to be applied wins

Sometimes, it is useful to have profile-specific properties that add to the active profiles rather than replace them. The spring.profiles.include property can be used to unconditionally add active profiles.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
pvpkiran
  • 25,582
  • 8
  • 87
  • 134
  • 8
    I know the link - it mentions "The spring.profiles.include property can be used to unconditionally add active profiles." - this description is ambiguous to me. But thats fine, is there any way then to just import settings from other profile, but not make them override the current one? – Daniel Szymatowicz Nov 18 '17 at 17:13
  • The quote makes no sense, how do you 'unconditionally add' without replacing? If the property is in both, which one is used? If it gets added, without conditions, then it is going to replace what's there. If it doesn't get added if it's already there, then that's a conditional add. – dan carter May 27 '22 at 03:21
  • Ok what the spring docs are talking about is unconditionally adding **profiles**. It's not talking about adding _properties_. Of course once the profile is added, the usual replacement rules apply about properties that exist in both (last one wins) – dan carter May 27 '22 at 03:24
9

Spring Boot 2.4 changes the mechanism for including multiple profiles to use a new profile groups feature, rather than using spring.profiles.include within the profile-specific document. This means that your configuration is no longer valid for new versions of Spring Boot, and would need to be changed.

That said, your use case does not seem to be a great fit for profile groups, as it's not really combining two profiles so much as overriding defaults. Therefore, I recommend using the approach suggested in another answer of putting the common and default properties in a shared application.yaml file, and only including the environment-specific values & overrides in the profile-specific documents.

application.yaml

spring:
  myproperty: 44 # Default value

application-bar.yaml

spring:
  myproperty: 55 # Override default

Note that Spring Boot supports multi-document files, so these can be combined into a single application.yaml file if desired:

spring:
  myproperty: 44 # Default value
---
spring.config.activate.on-profile: bar # These configs apply to the bar profile
spring:
  myproperty: 55 # Override default

Relevant 2.4 Changes

It is no longer possible to use spring.profiles.include within a profile-specific document as of Spring Boot 2.4, unless legacy mode is enabled using spring.config.use-legacy-processing=true. Per the 2.4 Spring Boot Config Data Migration Guide:

you can still use the spring.profiles.include property, but only in non profile-specific documents.

This approach has been replaced by the profile groups feature. Per the migration guide:

As discussed above, it’s no longer possible to use spring.profiles.include in a profile-specific document so this file isn’t valid.

Since this use-case is quite common, we’ve tried to provide another way to support it. In Spring Boot 2.4 you can use the “profile groups” feature.

This feature is documented in the Profile Groups section of the Spring Boot reference guide:

A profile group allows you to define a logical name for a related group of profiles.

For example, we can create a production group that consists of our proddb and prodmq profiles.

spring:
  profiles:
    group:
      production:
      - "proddb"
      - "prodmq"

Our application can now be started using --spring.profiles.active=production to active the production, proddb and prodmq profiles in one hit.

The migration guide points out that the spring.profile.group property cannot be used in profile-specific documents.

The spring.profile.group property cannot be used in profile-specific documents.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
  • Groups are just aliases to the list of profiles. They might give an impression of "documentation" but actually "useless". Just define profiles settings (in `application-$name.yml`) and activate required profiles in `application.yml` with `spring.profiles.active`. – gavenkoa Jul 25 '22 at 15:47
6

You could add a new profile in the application-bar.yaml:

spring.profiles.include: foo,foo-override
myproperty: 33

---
spring.profiles: foo-override
myproperty: 55

The order is: 33 in bar overridden by 44 in foo overridden by 55 in foo-override.

ywy
  • 69
  • 1
  • 4
2

Given:

  • The files: application-default.yml, application-foo.yml, application-bar.yml
  • myproperty: default in application-default.yml
  • myproperty: foo in application-foo.yml
  • myproperty: bar in application-bar.yml

I think these 2 use cases of using profiles are a little bit opposite in the meaning:

  1. In the most common case (-Dspring.profiles.active but no spring.profiles.include):

    1. When profile foo or boo are activated the properties from application-foo.yml (or application-bar.yml) will add/override the ones from application-default.yml.
    2. When profiles foo,bar are activated then properties from bar will add/override those from application-foo.yml and then those from application-default.yml.

    E.g: -Dspring.profiles.active=foo,bar the property from application-bar.yml wins (overrides) -> myproperty: bar

  2. In the second case (spring.profiles.include is used)

    1. The properties from the include statement add/overrides the properties from the application-*.yml files which uses spring.profiles.include

    I.e.: If application-boo.yml contains the spring.profiles.include=foo then properties from application-foo.bar adds/override properties from from application-bar.yml which add/override those from application-default.yml.

    On the other hand (I suppose) if application-boo.yml includes the spring.profiles.include=default,foo then properties from application-foo.yml will add/override those from application-default.yml which add/overrides those from application-bar.yml. So myproperty: bar. I wouldn't recommend the usage of default in combination with spring.profiles.include because this way it mixes the two cases and the override strategy is counterintuitive considering application-default.yml has a special treatment in springboot.

I also admit I am not at all a fan of the spring.profiles.active usage in application-*.yml files. I prefer to activate the profiles with system properties (maven included) or env variables. IMO it makes the whole profiles thing clearer to me.

If with my (herein above)reasoning I am on the wrong path please let me know.

jtonic
  • 609
  • 1
  • 7
  • 10