skip to content

How do OS environment variables map to Spring property names (relaxed binding), and what is SPRING_APPLICATION_JSON used for?

level: middleimportance: should knowfreq 50%

answer

  1. Dots -> underscores, UPPERCASE
  2. Double underscore __ = hyphen
  3. index: _0_ for list[0]
  4. SPRING_APPLICATION_JSON = one blob, flattened
  5. SAJ high priority, above env & -D

basics

~20 s

Because shells only allow uppercase letters, digits, and underscores, Spring Boot uses 'relaxed binding': an env var like SPRING_DATASOURCE_URL maps to spring.datasource.url (dots become underscores, uppercased). SPRING_APPLICATION_JSON lets you inject many properties at once as a single JSON string.

solid answer

~40 s

OS environment variables can't contain dots or hyphens, so Spring Boot's relaxed binding canonicalizes names: uppercase with underscores maps to the dotted lowercase property. `SPRING_DATASOURCE_URL` binds `spring.datasource.url`; a double underscore `__` represents a hyphen, and `_INDEX_` style suffixes handle list elements (e.g. `MY_LIST_0_`). This is the standard way to configure containerized apps, and env vars outrank the application.properties/yml files. `SPRING_APPLICATION_JSON` (or the `spring.application.json` system property) is a single environment variable holding a JSON document that Spring parses and flattens into properties — handy when a platform gives you one variable but you need to set a whole tree of config. It ranks high in precedence: above plain env vars and system properties, just below command-line arguments. Use env vars for individual overrides and SPRING_APPLICATION_JSON when you want to inject a structured block at once.

code

kotlin · 23 lines
kotlin
// application.yml expects:
//   spring.datasource.url
//   server.tomcat.max-connections
//   myapp.servers[0].host
//
// Set them purely from environment variables (relaxed binding):
//   export SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/app
//   export SERVER_TOMCAT_MAX__CONNECTIONS=200      # __ = hyphen
//   export MYAPP_SERVERS_0_HOST=node-a             # _0_ = list index 0
//
// Or inject a whole tree at once via SPRING_APPLICATION_JSON:
//   export SPRING_APPLICATION_JSON='{"server":{"port":9000},"myapp":{"name":"orders"}}'
//   java -jar app.jar    # server.port=9000, myapp.name=orders

import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "myapp")
data class MyAppProps(
    val name: String = "",
    val servers: List<Server> = emptyList(),
) {
    data class Server(val host: String = "")
}

go deeper

for a junior

Knows env vars can override config; may not know the exact relaxed-binding mapping.

for a middle

Should state the dot->underscore/uppercase mapping, the __ = hyphen rule, and what SPRING_APPLICATION_JSON does.

for a senior

Should place both sources in the precedence order and handle list-index binding and shell escaping.

for a principal

Chooses between per-var overrides vs a single JSON blob per platform constraints, and pairs env config with secret management.

## The problem relaxed binding solves Property names are conventionally dotted, lowercase, hyphenated: `spring.datasource.url`, `server.tomcat.max-connections`. But OS environment variables in most shells may only use **uppercase letters, digits, and underscores** — no dots, no hyphens, no lowercase reliably. Spring Boot bridges this with **relaxed binding** (implemented around `@ConfigurationProperties` binding and `SystemEnvironmentPropertySource`). ## The canonical mapping rules To set a property from an environment variable: - **Uppercase the property name.** - **Replace dots (`.`) with underscores (`_`).** - **Remove hyphens or replace a hyphen with `_`** — more precisely, a **double underscore (`__`)** in the env var is treated as an escaped hyphen. - **List/array indices**: use an underscore-bounded numeric index. `MY_APP_SERVERS_0_HOST` maps to `my.app.servers[0].host`. Examples: | Environment variable | Property | |---|---| | `SPRING_DATASOURCE_URL` | `spring.datasource.url` | | `SERVER_PORT` | `server.port` | | `SERVER_TOMCAT_MAX__CONNECTIONS` | `server.tomcat.max-connections` | | `MYAPP_SERVERS_0_HOST` | `myapp.servers[0].host` | Relaxed binding is **one-directional at read time**: it works cleanly for `@ConfigurationProperties`-bound beans and for env vars; a few edge cases (like `@Value` with an exact key) are stricter, so prefer `@ConfigurationProperties` when relying on env-var binding. ## Precedence reminder OS environment variables sit **above** the config-data files (application.properties/yml) but **below** Java system properties and SPRING_APPLICATION_JSON. So `SERVER_PORT` overrides the yml but is itself overridden by `-Dserver.port` and by `--server.port`. ## SPRING_APPLICATION_JSON Sometimes a platform only lets you set **one** environment variable but you need to inject a whole tree of configuration. `SPRING_APPLICATION_JSON` accepts a JSON document as its value; Spring Boot parses and **flattens** it into individual properties. Provide it as: - The `SPRING_APPLICATION_JSON` **environment variable**, or - The `spring.application.json` **system property** (`-Dspring.application.json=...`), or - A JNDI variable `java:comp/env/spring.application.json`. Example: `SPRING_APPLICATION_JSON='{"server":{"port":9000},"myapp":{"name":"orders"}}'` sets `server.port=9000` and `myapp.name=orders`. It is a **high-priority** source — above OS env vars and system properties, just below command-line arguments — so it overrides the files and plain env vars. ## Gotchas - **Quoting/escaping**: the JSON must be valid and correctly quoted for your shell; malformed JSON causes startup failure. - **Precedence surprise**: engineers assume the JSON is 'just an env var' and rank it low — it's actually near the top. - **Relaxed binding only accepts the canonical env-var form**; arbitrary casing like `Spring_Datasource_Url` won't reliably bind. - **List indices** must use the numeric-underscore form; forgetting the trailing structure yields unbound values. - **Secrets**: env vars and SPRING_APPLICATION_JSON are visible in the process environment — combine with a secrets manager for sensitive values, and remember env vars override files, which is exactly why they're the container override mechanism. ## When to use which Use **individual env vars** for a handful of per-environment overrides (the 12-factor default). Use **SPRING_APPLICATION_JSON** when you must inject a structured block through a single variable (some PaaS/CI systems). Use **command-line args** for ad-hoc launches. Keep static defaults in the yml.

  • Given the property server.tomcat.max-connections, what environment variable name sets it?
    SERVER_TOMCAT_MAX__CONNECTIONS — dots become single underscores, the whole name is uppercased, and the hyphen in 'max-connections' is represented by a double underscore.
  • Does SPRING_APPLICATION_JSON override a value set in an ordinary environment variable?
    Yes. SPRING_APPLICATION_JSON is a higher-priority source than plain OS environment variables (and Java system properties), so a key present in both takes its value from the JSON blob.

saying these in an interview costs you the question

  • Claiming env vars can contain dots so SPRING.DATASOURCE.URL works
  • Thinking SPRING_APPLICATION_JSON is low priority because it's an env var
  • Using a single underscore to represent a hyphen (it's a double underscore)
  • Assuming relaxed binding accepts arbitrary mixed-case env var names

context