Spring Boot configuration best practices
Introduction
A service that is easy to run locally, in CI, and in the cloud is mostly about good configuration habits: what lives in the repo, what is injected at deploy time, and what never is printed in logs. This post sets habits before we wire PostgreSQL and later AI keys.
Real-world explanation
- 12-factor style: config in the environment, not in the code. Spring maps env vars to relaxed binding:
SPRING_DATASOURCE_URL→spring.datasource.url - Profiles (
dev,prod,test) let you change a small set of beans or properties without forking the codebase. - @ConfigurationProperties (optional) binds a prefix to a typed Java class—handy for reviewable, validated settings.
Short example: typed config
AppProperties.java (package com.example.demoservice.config):
package com.example.demoservice.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app")
public record AppProperties(
String publicBaseUrl
) { }Enable in a @Configuration class: @EnableConfigurationProperties(AppProperties.class).
application.yml:
app:
public-base-url: "http://localhost:8080"Practical rules
- Never commit real passwords or API keys. Use env or a secret store; Spring reads them the same.
- Log the active profile and datasource host in startup without credentials (Boot’s datasource auto-config is careful; you still should not
printlnthe password). - Default to fail if a required value is missing in
prod—@Value("${foo}")with no default will fail fast, which is what you want for critical settings. - Validation of properties: use
@Validated+@NotBlankon the@ConfigurationPropertiestype when the field must exist in prod.
Common mistakes
- Giant
application.ymlwith 300 lines: split byspring.config.import(Boot 2.4+) or profile files ddl-auto: updatein production without a migration strategy- Relying on
localhostin default yml and forgetting to override in staging—treatlocalhostas a dev-only default
Best practices
- One name for the app:
spring.application.name(helps in logs, tracing later) - JPA logging: in dev,
org.hibernate.SQLto DEBUG temporarily; in prod, not in steady state - Feature flags for AI routes: simple
app.ai.enabled: falsein prod until ready (covered in AI posts)
Final summary
Externalize secrets, name your application, use profiles, avoid dangerous DDL defaults, and type frequent settings. Next article: run PostgreSQL locally and point Boot at it.