Skip to main content
4 of 7
deleted 2 characters in body
simbo1905
  • 1.2k
  • 5
  • 14

The typical approach is to use a folder per environment that contains identically named files:

conf/dev/application.properties
conf/test/application.properties
conf/uat/application.properties
conf/prod/application.properties

Then parameterize the @PropertySource to use an environment variable such as "env":

@PropertySource("file:${catalina.base}/conf/${env}/application.properties") 

Now you simply need to ensure that in each environment there is an environment variable defined called "env" that is set correctly (i.e, dev, test, uat or prod). You haven't stated how your launch your application (j2ee server launch script on linux? springboot docker container?) but it shouldn't be a problem to set the environment variable in each environment.

Rather than load files from "${catalina.base}" you can load files from the classpath with something like:

// see https://stackoverflow.com/a/26387933/329496
@PropertySource("classpath:${env}/application.properties")

If you are building with maven you can create the folders under src/main/resources and they will be copied into the classpath:

src/main/resources/dev/application.properties
src/main/resources/test/application.properties
src/main/resources/uat/application.properties
src/main/resources/prod/application.properties

That simplified things quite a lot. Jenkins doesn't need to do anything at all as the same build/release artefact can be run in any and every environment. It is usually an anti-pattern to have Jenkins have to do extra work to reconfigure an application to work in a particular environment. Rather you should aim to have a single Jenkins release job build an artefact that can be simply copied between environments.

There is one downside to such an approach. If you want to create a new environment you have to add a new file into source control and create a new release artefact. With legacy technologies setting up new environments is a lot of work (e.g., commissioning VMs and databases) such that this "extra step" of adding a file to the code doesn't seem like a problem.

With more modern cloud-native technologies like Kubernetes, you can set up environments in seconds, and "on-demand". In which case you don't want to be pre-specifying your environments within your codebase. Rather you should follow a 12factor.net approach and define every single property as an environment variable and have spring just use them directly. If you look at the current springboot documentation it uses environment variables by default. This means if you haven't defined a @PropertySource but you are using things like:

@Value("${name}")
private String name;

they will can set from the environment variable of the same name but using all caps. This then moves the problem to "how do we make sure that the environment variables are correctly set up in each environment". Technologies that make it easy to spin up environments in seconds (kubernetes, cloudfoundry, swarm) also typically make it easy to manage environment variables. With kubernetes you create a "ConfigMap" (e.g. "my-app-properties") and have it mounted as the environment variables for the application. Each environment can then be a separate namespace (possibly on a shared cluster for dev/test but a dedicated cluster for prod) that has its own "my-app-properties" configuration object.

You can put these settings under source control but not in the application source repo. Rather you can take an "infrastructure as code" approach where everything that runs the application (e.g., all scripts and yaml to set up the environments) in it's own separate git repo. You can then set up a configuration deploy job that is triggered by a git webhook on the configuration repo. This can push out the new environment variables. That allows you to have continuous deployment of configuration changes that are independent of the continuous deployment of application code.

If you are using Kubernetes then Helmfile is an excellent choice for that is it won't update anything in kubernetes that hasn't changed. This means it is safe to reapply all the configuration in the git repo on every push to a protected master branch as it will only update objects that need to be updated.

simbo1905
  • 1.2k
  • 5
  • 14