Spring Boot - Externalized Configuration

What it is: Spring Boot lets you keep settings (DB URLs, ports, feature flags, etc.) outside your code/jar so you can change behavior without rebuilding.

Why it’s useful

  • Different config per environment (dev/stage/prod) via profiles.
  • Override at deploy time using env vars or CLI without touching files.
  • Type-safe binding of config into POJOs with validation.

Configuration sources & precedence

(Higher items override lower ones if the same key is defined.)

  1. Command line args
    java -jar app.jar --server.port=9090 --app.feature.enabled=true
  2. Java system properties: -Dserver.port=9090
  3. OS environment variables (map . to _, uppercase): SPRING_DATASOURCE_URL=jdbc:...
  4. SPRING_APPLICATION_JSON (inline JSON env var)
  5. Config files (properties or YAML), searched in: ./config/, ./, classpath:/config/, classpath:/
    Common filenames: application.yml, application.properties, plus profile variants.
  6. Defaults (programmatic): SpringApplication.setDefaultProperties(...)

You can also point to custom paths:

--spring.config.location=file:/etc/myapp/,classpath:/extra/

Profiles (per-environment config)

Activate

  • CLI: --spring.profiles.active=dev
  • Env var: SPRING_PROFILES_ACTIVE=prod

Files

  • application.yml (common)
  • application-dev.yml, application-prod.yml (overrides)

Multi-document YAML example

# application.yml
app:
  title: "MyApp"

---
spring.config.activate.on-profile: dev
server.port: 8081
app.title: "MyApp (DEV)"

---
spring.config.activate.on-profile: prod
server.port: 8080

Reading config in code

A) Type-safe binding with @ConfigurationProperties (recommended)

Supports lists, maps, durations, data sizes, and validation with relaxed binding.

YAML

app:
  storage:
    location: /var/data
    max-size: 256MB
    tags: [fast, ssd]

Java class

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
import org.springframework.util.unit.DataSize;

@Validated
@ConfigurationProperties(prefix = "app.storage")
public class StorageProps {
  @NotBlank private String location;
  private DataSize maxSize; // parses "256MB" automatically
  private List<String> tags;
  // getters/setters
}

Enable scanning (Boot 2.2+)

@SpringBootApplication
@ConfigurationPropertiesScan // picks up @ConfigurationProperties classes
public class App { }

Inject/use it

@Service
public class StorageService {
  private final StorageProps props;
  public StorageService(StorageProps props) { this.props = props; }
}

B) Quick injections with @Value

@Value("${app.feature.enabled:false}")
private boolean enabled;

Good for one-offs; prefer @ConfigurationProperties for grouped settings.

Environment variable mapping (relaxed binding)

  • Property: spring.datasource.url
  • Env var: SPRING_DATASOURCE_URL

Dots β†’ underscores, kebab/camel accepted, case-insensitive.

Config Data API (Boot 2.4+)

Import extra files/dirs/secrets declaratively:

spring:
  config:
    import:
      - optional:file:/etc/myapp/           # folder
      - optional:configtree:/run/secrets/   # k8s secrets as key-per-file

Tip: Prefix with optional: so startup won’t fail if a location is missing.

Secrets & external stores

  • Prefer env vars, mounted secrets (configtree), or Spring Cloud Config/Vault/AWS Secrets Manager.
  • Spring Boot doesn’t encrypt values by itself; use Vault or solutions like Jasypt if required.

Handy patterns

Placeholders & defaults

app:
  api-url: "${API_URL:https://api.example.com}"  # env var override or default

Profile-specific beans

@Profile("prod")
@Bean DataSource prodDs(...) { ... }

Validation on configuration

@Validated
@ConfigurationProperties("app.mail")
class MailProps {
  @jakarta.validation.constraints.NotBlank String host;
  @jakarta.validation.constraints.Min(1) int poolSize = 5;
}

Quick checklist

  • Shared config in application.yml; overrides in application-<profile>.yml.
  • Activate with SPRING_PROFILES_ACTIVE.
  • Use @ConfigurationProperties + @ConfigurationPropertiesScan for structured, validated settings.
  • Override at deploy time with env vars or CLI args (highest precedence).
  • Avoid hardcoding secrets; inject securely via env/secrets managers.
Back to blog

Leave a comment