Environment Variables In Spring Boot Application
Tags: java, springboot, 12factorapps, configurations
Published: 2020-12-15 15:33:43

Spring Boot lets you externalize [configurations](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config) for our application. Lets take a simple application which connects to database and perform some operations on it. Standard configuration for this kind of app would be the database related details. Below is one sample `properties` file

```
spring.datasource.url=${JDBC_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
```


Like this we might have more properties specific to our application also which we want to have it as dynamic configurations that can be passed when application startup. In order to have these have different across different environments like dev,staging,production. Spring boot provides us with [option ](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-files-profile-specific) to have environment specific files with different values for different environments.

Below are few common practices developers follow which has its own pros and cons

*  Commit secrets in these files as part of the code base
*  Commit only local configuration in the code base and as part of deployment pipeline copy environment specific files from different repository and build the package

If we are having separate profile files then we might need to have separate start-up scripts for each to ensure right profile details are passed. If we are copying files at the time of the build for any change in secrets or configurations we might need to build the package again to take effect.

According to [12factor](https://12factor.net/config) apps its good practice to store configurations in environments.

## How can we achieve this?

* We can avoid multiple property files and have only one file as below

```
spring.datasource.url=${JDBC_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
```

The above helps in multiple ways

* No need to rebuild the package again if there is any change in configuration.
* One common properties file versioned as part of code base.
* Build pipelines would be generic. Same builds can be used across all environments

### When running as containers

If we are running our application as containers we need to pass the environment variables while starting the application. This can be taken care by a deployment pipeline.If there is a change in value just a restart of the container is enough

### When running as fat jars

If we are running our application as normal jars then best practice would be to set the environment variables as part of the host machine and start the application. 


### What about local development/tests

For tests I would recommend to have dedicated properties file. For local development we can do it two ways.
* Have one `.env` file in local and `source` them before starting the application
* We can give default values to properties if not present in environment as `spring.datasource.username=${DB_USERNAME:username}`
 

The key thing to note here is the deployment process. I will cover a detailed blog on how the deployment pipeline can be done for such a setup of spring boot application.