YAML vs properties, how profiles really work (activation, groups, multi-document files), why profiles, managing secrets across environments, centralised config for microservices, i18n in Boot, relaxed binding done right, dev-vs-prod differences, and routing between multiple data sources at runtime.
Published September 25, 2026
Configuration problems cause a surprising share of production incidents: the wrong profile, a leaked secret, a property that silently didn't bind. Show that you know where Boot looks for configuration, in what order, and how to keep secrets out of Git.
.properties, and what are its limitations?Short answer: YAML is hierarchical, so it's less repetitive and more readable for nested and list configuration. It supports multi-document files (---), for per-profile sections. Its limitations:
on/no/yes, leading zeros, and version numbers like 1.10 read as a float.@PropertySource doesn't load YAML files (without a custom factory).payments:
timeout: 3s
providers:
- name: razorpay
url: https://api.razorpay.example
- name: stripe
url: https://api.stripe.example
Key points to cover:
version: "1.10").Short answer: A profile is a named set of beans and properties that's active only when the profile is:
application-{profile}.yml files, or sections of a multi-document YAML file guarded by spring.config.activate.on-profile. They override the default application.yml.@Profile("prod"), with expressions such as !prod and cloud & kafka.spring.profiles.active, through a command-line argument, the SPRING_PROFILES_ACTIVE environment variable, a system property, or @ActiveProfiles in tests. Groups (spring.profiles.group.prod=db,observability) bundle profiles together.spring:
profiles:
group:
prod: "postgres,observability"
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:h2:mem:shop
Learn it in depth → Configuration Management
Short answer: The same artifact can then run in every environment. Only the configuration changes, so you test exactly the binary you ship. Profiles switch data sources, endpoints, logging levels, feature toggles, mock or real integrations, and dev-only beans (such as seed data), without code changes or rebuilds.
Key points to cover:
Short answer:
spring.config.import=configtree:/run/secrets/) reads Kubernetes secret volumes as properties.spring.config.import./env and /configprops.spring:
config:
import: "optional:configtree:/run/secrets/,aws-secretsmanager:prod/shop/db"
datasource:
password: ${db-password}
Key points to cover:
Short answer:
{cipher} values).@RefreshScope, Spring Cloud Bus).Key points to cover:
Short answer: Boot auto-configures a MessageSource when it finds messages.properties (plus messages_hi.properties, messages_fr.properties…) on the classpath. The request's locale comes from a LocaleResolver: the default uses Accept-Language, while session or cookie resolvers with a LocaleChangeInterceptor allow switching languages. Messages are resolved in templates (#{…}), in code (messageSource.getMessage(...)), and in Bean Validation error messages.
spring:
messages:
basename: messages,errors
fallback-to-system-locale: false # fall back to messages.properties, not the server's OS locale
Key points to cover:
ProblemDetail titles and details can be resolved from the MessageSource.Short answer: When binding to @ConfigurationProperties, Boot accepts several spellings of the same property name, so the same setting can come from YAML, a system property or an environment variable:
| Source | Form for payments.base-url |
|---|---|
| properties/YAML (canonical, kebab-case) | payments.base-url |
| camelCase | payments.baseUrl |
| underscore | payments.base_url |
| environment variable | PAYMENTS_BASEURL or PAYMENTS_BASE_URL (dots → underscores, upper case) |
Common trap: saying server-port is the same as server.port. Dots separate the levels of the hierarchy, and relaxed binding only varies the form within each name segment. Also, @Value supports only limited relaxed binding. Prefer @ConfigurationProperties.
Short answer:
application.yml.application-dev.yml and application-prod.yml, or better, inject environment values through environment variables.@Profile: a stub payment gateway in dev, and a real one in prod.SPRING_PROFILES_ACTIVE.@Configuration
class GatewayConfig {
@Bean @Profile("dev") PaymentGateway fakeGateway() { return new FakePaymentGateway(); }
@Bean @Profile("!dev") PaymentGateway realGateway(PaymentProperties p) { return new RazorpayGateway(p); }
}
Key points to cover:
Short answer:
AbstractRoutingDataSource, which wraps several target DataSources and chooses one per connection through determineCurrentLookupKey().ThreadLocal context, set by a filter or interceptor from the request (a header, JWT claim or subdomain), and cleared in finally.public class RegionRoutingDataSource extends AbstractRoutingDataSource {
@Override protected Object determineCurrentLookupKey() { return RegionContext.get(); } // "EU", "APAC"
}
@Bean
DataSource dataSource(DataSource euDs, DataSource apacDs) {
var routing = new RegionRoutingDataSource();
routing.setTargetDataSources(Map.of("EU", euDs, "APAC", apacDs));
routing.setDefaultTargetDataSource(euDs);
return routing;
}
// in a OncePerRequestFilter
RegionContext.set(resolveRegion(request));
try { chain.doFilter(request, response); } finally { RegionContext.clear(); }
Key points to cover:
@Async and other threads need the context propagated.TransactionSynchronizationManager.isCurrentTransactionReadOnly() and LazyConnectionDataSourceProxy.Q: In what order does Spring Boot load configuration? A: From lowest to highest priority (simplified):
application.yml inside the JAR../config/).Later sources override earlier ones for the same key.
Q: How do you validate configuration at startup?
A: Annotate the @ConfigurationProperties class with @Validated, and add constraints (@NotBlank, @Min, @DurationMin). A misconfigured deployment then fails immediately with a clear message, instead of misbehaving later.
Q: How do you refresh configuration without restarting?
A: With Spring Cloud Config, use @RefreshScope beans plus /actuator/refresh (or Spring Cloud Bus to broadcast the refresh). @ConfigurationProperties beans are rebound on refresh. Many teams prefer immutable configuration plus a rolling restart, because it's simpler to reason about.
Q: Where should feature flags live?
A: For simple on/off switches, in properties with @ConditionalOnProperty (read at startup). For runtime toggles, percentage rollouts and per-user targeting, use a feature-flag service (Unleash, LaunchDarkly, Flagsmith) through the OpenFeature SDK.