Modern containerised 12 factor applications are expected to derive their configuration from environment variables. While Spring Boot does import its common properties from environment variables, sometimes you need to interpolate several variables together, e.g. to form a URL.

This post is part of the “Spring Boot Primer” series.

Rather than littering your code with environment variables, simple variable interpolation in your application.yml or application.properties can be used. You can also override the default values for built-in common properties this way:

application.ymlapplication.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:

  datasource:
    url: jdbc:${DB_VENDOR:mariadb}://${DB_ADDR:localhost}:${DB_PORT:3306}/${DB_NAME:backend}
    username: ${DB_USER:backend}
    password: ${DB_PASSWORD:XfeCEtSOFL91QpeyDxQnkRattHWzufTdDB1Pn5iB4}
    driver-class-name: ${DB_DRIVER:org.mariadb.jdbc.Driver}

  session:
    store-type: ${SESSION_STORE_TYPE:redis}

  redis:
    host: ${SESSION_HOST:localhost}
    password: ${SESSION_PASSWORD:}
    port: ${SESSION_PORT:6379}

To allow you to set sensible defaults, so that your application can be started straight from your IDE with no additional configuration, you can also embed your development environment defaults, with the normal BASH-style syntax:

${DB_PASSWORD:XfeCEtSOFL91QpeyDxQnkRattHWzufTdDB1Pn5iB4}

At development-time (either running the app locally in your IDE / CLI or in Docker), you don’t need to provide any additional configuration; making set-up for new developers on your team very rapid.

At production-time, all of these properties can easily be set via your docker-compose.yml:

docker-compose-production.ymldocker-compose-production.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
services:
  backend:
    image: petewoods/spring-rest-example:latest
    deploy:
      replicas: 2
    volumes:
      - backend-data:/var/lib/data
    environment:
      DB_VENDOR: 'mariadb'
      DB_ADDR: 'db'
      DB_NAME: 'backend'
      DB_USER: 'backend'
      DB_PASSWORD: "${MYSQL_PASSWORD}"
      DB_DRIVER: 'org.mariadb.jdbc.Driver'
      SESSION_HOST: 'cache'
      SESSION_PASSWORD:
      SESSION_PORT: 6379
      MEDIA_LOCATION: 'file:/var/lib/data/'
      GOOGLE_CLIENT_ID: "${GOOGLE_CLIENT_ID}"
      GOOGLE_CLIENT_SECRET: "${GOOGLE_CLIENT_SECRET}"
      # AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
      # AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
      # CLOUDWATCH_METRICS_ENABLED: 'true'
      # CLOUDWATCH_METRICS_NAMESPACE: 'production-spring-rest-example'
      # MEDIA_LOCATION: 's3://my-bucket'
    ports:
      - '8080:8080'

then you can move your configuration in an environment file, and run as follows:

(. production.env && docker stack deploy --prune -c docker-compose.yml -c docker-compose-production.yml spring-rest-example)

or (even better) pull out into a secure key store such as LastPass, and interpolate in using something like LastPass CLI.