Skip to content
Spring Boot sb config 4 min read

Externalized Configuration

Externalized configuration is what lets one Spring Boot artifact run unchanged across every environment. Settings live outside the compiled code — in YAML, environment variables, command-line arguments, or an imported config server — and Spring merges them from many sources in a well-defined precedence order. Understanding that order is the key to predictable configuration.

Why externalize

The goal is a single immutable JAR or container image that you promote from dev to staging to production. Defaults ship inside the artifact; each environment overrides only what differs. Nothing environment-specific or secret is baked into the build.

Property-source precedence

Spring Boot loads many sources and lets higher-priority ones override lower ones. The list below runs from highest priority (wins) to lowest (most easily overridden). This is the abbreviated, most-used view of Spring Boot’s full ordering.

PrioritySource
HighestDevtools global settings (~/.config/spring-boot)
@TestPropertySource / test properties
Command-line arguments (--server.port=9090)
SPRING_APPLICATION_JSON (inline JSON)
ServletConfig / ServletContext init params
JNDI attributes (java:comp/env)
Java System properties (-Dserver.port=9090)
OS environment variables (SERVER_PORT=9090)
Profile-specific application-<profile>.yml (outside jar)
Profile-specific application-<profile>.yml (inside jar)
Base application.yml (outside jar)
LowestBase application.yml (inside jar), @PropertySource, defaults

This precedence is exactly what makes the same JAR portable: ship defaults in YAML, then override per environment with env vars or command-line args.

Command-line arguments

Any property can be passed as a --key=value argument. These sit near the top of the precedence list, so they override almost everything.

java -jar app.jar --server.port=9090 --spring.profiles.active=prod

SPRING_APPLICATION_JSON lets you supply a whole tree as inline JSON, useful in CI and PaaS environments:

java -jar app.jar \
  --spring.application.json='{"app":{"name":"Shop","mail":{"port":587}}}'

OS environment variables

Spring’s relaxed binding maps environment variables to property keys: uppercase the key and replace dots and dashes with underscores. So spring.datasource.url is satisfied by SPRING_DATASOURCE_URL.

export SERVER_PORT=9090
export SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shop
export APP_MAIL_MAX_ATTEMPTS=5      # binds to app.mail.maxAttempts
java -jar app.jar

This convention is the backbone of containerized and cloud deployments, where connection strings and credentials arrive as variables rather than files.

Tip: Environment-variable binding works for both @Value placeholders and @ConfigurationProperties. For grouped settings, prefer the typed approach.

Importing additional config

spring.config.import pulls in extra configuration sources during startup. It can load extra files, directories, or — through Spring Cloud — a config server or secrets manager.

# application.yml
spring:
  config:
    import:
      - optional:file:./config/local.yml   # optional: won't fail if missing
      - optional:configtree:/etc/secrets/   # one file per key (Kubernetes Secrets)

The configtree: resource reads a directory where each file’s name is a property key and its contents are the value — the standard layout for Kubernetes Secrets and Docker secrets mounted at /run/secrets/.

.env-style files

There is no automatic .env support, but you can import a properties file explicitly:

spring:
  config:
    import: optional:file:.env[.properties]

The [.properties] extension hint tells Spring how to parse a file whose name lacks a recognized suffix.

Handling secrets

Never commit passwords or API keys to application.yml. Keep only safe defaults in version control and supply secrets at runtime.

# application.yml — placeholder only, real value injected at runtime
spring:
  datasource:
    username: ${DB_USER}
    password: ${DB_PASSWORD}
ApproachWhere secrets liveBest for
Environment variablesProcess environmentSimple deployments, 12-factor apps
configtree: importMounted secret filesKubernetes / Docker secrets
Config serverSpring Cloud Config / VaultMany services, central rotation
Cloud secrets managerAWS / GCP / AzureManaged cloud platforms

Warning: Anything in application.yml inside the JAR is shipped to every environment and visible to anyone with the artifact. Treat it as public, and externalize every credential.

Configuration in containers

In a Docker or Kubernetes deployment, the image is immutable and configuration is injected at run time.

FROM eclipse-temurin:17-jre
COPY target/app.jar /app/app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
docker run \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shop \
  -e DB_USER=shop -e DB_PASSWORD=$DB_PASSWORD \
  myorg/shop:1.4.0

For Kubernetes, mount a ConfigMap as files and a Secret as a config tree, then reference them via spring.config.import. This keeps the same image identical across clusters.

Best Practices

  • Ship safe defaults in application.yml; override per environment with env vars or command-line args.
  • Understand the precedence order so you always know which source wins.
  • Externalize every secret — use env vars, config trees, or a secrets manager, never committed YAML.
  • Use spring.config.import with optional: so a missing local override does not break startup.
  • Bind grouped settings with @ConfigurationProperties for type safety.
  • Keep the artifact immutable; the same image should run in every environment.
Last updated June 13, 2026
Was this helpful?