Configuration & Actuator

application.yml structure, externalized config and profiles, and Actuator health checks and metrics in production.

application.yml structure

Spring Boot centralizes configuration in application.yml (or application.properties) at src/main/resources. YAML's nesting maps naturally onto the dotted property names Spring Boot uses internally:

YAML
server:
  port: 8080

spring:
  application:
    name: bookstore-service
  datasource:
    url: jdbc:postgresql://localhost:5432/bookstore
    username: app_user
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    root: INFO
    com.example.bookstore: DEBUG

The equivalent .properties form is flatter but expresses the same structure:

Properties
server.port=8080
spring.application.name=bookstore-service
spring.datasource.url=jdbc:postgresql://localhost:5432/bookstore
spring.datasource.username=app_user
spring.datasource.password=${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate
logging.level.root=INFO

${DB_PASSWORD} above pulls from an environment variable at startup — never commit real credentials into application.yml.

Externalized configuration and profiles

Spring Boot resolves the same property from multiple sources, in a defined precedence order (highest wins): command-line arguments, environment variables, then application-{profile}.yml, then the base application.yml. This lets the exact same packaged JAR run correctly across dev, staging, and production by changing only the environment it's deployed into — no rebuild required.

YAML
# application.yml — shared defaults
spring:
  jpa:
    show-sql: false
YAML
# application-dev.yml — active only when the "dev" profile is active
spring:
  datasource:
    url: jdbc:h2:mem:devdb
  jpa:
    show-sql: true
Bash
java -jar app.jar --spring.profiles.active=dev

Type-safe configuration classes are the recommended way to consume a related group of properties, instead of scattering @Value annotations:

YAML
app:
  mail:
    from-address: noreply@bookstore.example
    retry-count: 3
Java
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(String fromAddress, int retryCount) {
}
Java
@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig {
}

Spring Boot Actuator

Actuator exposes production-facing operational endpoints — health, metrics, environment info — without you writing any of that plumbing yourself. Add it as a dependency:

HTML
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

By default, only /actuator/health is exposed over HTTP. Enable more endpoints deliberately in configuration:

YAML
management:
  endpoints:
    web:
      exposure:
        include: health, info, metrics
  endpoint:
    health:
      show-details: when-authorized
Bash
curl localhost:8080/actuator/health
JSON
{ "status": "UP" }

With show-details enabled and a database configured, the same endpoint reports on each dependency Spring Boot knows how to check:

JSON
{
  "status": "UP",
  "components": {
    "db": { "status": "UP", "details": { "database": "PostgreSQL" } },
    "diskSpace": { "status": "UP" }
  }
}

/actuator/metrics exposes counters and timers (request latency, JVM memory, thread pool usage) that feed directly into monitoring systems like Prometheus.

Why this matters in production: a load balancer or orchestrator (Kubernetes, ECS) needs a reliable way to know whether an instance is actually healthy before routing traffic to it, and whether to restart it if it isn't — that's exactly what a /health liveness/readiness check is for. Metrics endpoints feed dashboards and alerting, turning "is the service slow right now?" from a guess into a graph. Actuator is what makes a Spring Boot service observable without hand-building any of this from scratch.

Common mistakes

  • Exposing every Actuator endpoint (include: "*") on a public-facing port — endpoints like /actuator/env or /actuator/heapdump can leak secrets or internal details and should be restricted or placed behind authentication.
  • Committing real secrets directly into application.yml instead of referencing environment variables or a secrets manager.
  • Never checking /actuator/health from the deployment platform's health-check configuration, leaving a struggling instance in rotation instead of being pulled out automatically.
  • Scattering many individual @Value properties instead of grouping a related set into one @ConfigurationProperties class.