Back to Blog
spring boot application.propertiesapplication.ymlspring boot configurationexternal configurationapplication profilesspring boot environment variables

Spring Boot application.properties vs application.yml: Complete Configuration Guide

Master Spring Boot configuration: Learn the differences between application.properties and application.yml, best practices, profiles, and advanced techniques for managing environment-specific settings.

J

JOptimize Team

April 25, 2026· 5 min read

Spring Boot application.properties vs application.yml: Complete Configuration Guide

You've just deployed your Spring Boot application to production, and suddenly it crashes because the database URL is wrong.

Or worse: your logging level is set to DEBUG, and now your logs are filling up disk space.

These aren't code bugs. They're configuration problems. And they happen because most developers don't fully understand how Spring Boot loads and manages configuration.

In this guide, you'll learn exactly how to configure Spring Boot the right way—so your application works in development, staging, AND production without code changes.


The Basics: What is application.properties?

application.properties is a file in your src/main/resources folder that tells Spring Boot how to behave.

Instead of hardcoding values like database URLs, ports, or API keys in your code, you put them in this file:

# application.properties server.port=8080 spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret123 spring.jpa.hibernate.ddl-auto=update

Then in your code, you access these values:

@Component public class MyComponent { @Value("${server.port}") private int port; @Value("${spring.datasource.url}") private String dbUrl; public void init() { System.out.println("Server running on port: " + port); System.out.println("Database: " + dbUrl); } }

Why is this important?

  • No hardcoding secrets in code
  • Easy to change settings without recompiling
  • Different configurations for dev/staging/production
  • Environment variables can override values

application.yml vs application.properties: What's the Difference?

Both do the same thing, but with different syntax.

application.properties (flat structure)

spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret123 spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true

application.yml (hierarchical structure)

spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: secret123 jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true

Which is better?

Aspectapplication.propertiesapplication.yml
ReadabilityFlat, repetitiveHierarchical, cleaner
NestingLots of dotsClear hierarchy
MaintenanceHard to scanEasy to scan
Learning curveSimplerSteeper
Industry standardOlder projectsModern projects

Recommendation: Use application.yml for new projects. It's more readable and less error-prone.


Profile-Specific Configuration (The Real Power)

The real magic happens when you use application profiles. This lets you have different configurations for different environments.

Step 1: Create profile-specific files

src/main/resources/
  ├── application.yml (default, shared settings)
  ├── application-dev.yml (development)
  ├── application-staging.yml (staging)
  └── application-prod.yml (production)

Step 2: Define the base application.yml (shared settings)

# application.yml spring: application: name: my-app jpa: show-sql: false properties: hibernate: format_sql: true server: servlet: context-path: /api

Step 3: Create development profile

# application-dev.yml spring: datasource: url: jdbc:mysql://localhost:3306/mydb_dev username: root password: dev_password jpa: hibernate: ddl-auto: create-drop # Recreate DB on startup show-sql: true h2: console: enabled: true logging: level: root: INFO com.myapp: DEBUG server: port: 8080

Step 4: Create production profile

# application-prod.yml spring: datasource: url: jdbc:mysql://prod-db-server:3306/mydb_prod username: ${DB_USERNAME} # From environment variable password: ${DB_PASSWORD} # From environment variable hikari: maximum-pool-size: 20 jpa: hibernate: ddl-auto: validate # NEVER recreate in production show-sql: false logging: level: root: WARN com.myapp: INFO server: port: 8080 ssl: enabled: true key-store: /etc/ssl/keystore.jks key-store-password: ${KEYSTORE_PASSWORD}

Step 5: Activate the profile

Option A: application.yml (recommended)

# application.yml spring: profiles: active: dev # or staging, prod

Option B: System property

java -Dspring.profiles.active=prod -jar app.jar

Option C: Environment variable

export SPRING_PROFILES_ACTIVE=prod java -jar app.jar

Option D: application.properties

# application.properties spring.profiles.active=dev

Externalizing Configuration (Environment Variables)

Hardcoding database passwords in files is a security nightmare. Use environment variables instead.

In application.yml

spring: datasource: url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mydb} username: ${DB_USERNAME} password: ${DB_PASSWORD}

The syntax ${VARIABLE_NAME:default_value} means:

  • Use the environment variable VARIABLE_NAME
  • If not set, use default_value

Set environment variables

Docker:

FROM openjdk:17 COPY target/app.jar app.jar ENV DB_HOST=db-server ENV DB_USERNAME=prod_user ENV DB_PASSWORD=secret123 ENTRYPOINT ["java", "-jar", "app.jar"]

Kubernetes:

apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DB_HOST: "postgres.default.svc.cluster.local" DB_PORT: "5432" --- apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: DB_USERNAME: cHJvZF91c2Vy # base64 encoded DB_PASSWORD: c2VjcmV0MTIz # base64 encoded --- apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: template: spec: containers: - name: app image: my-app:1.0 envFrom: - configMapRef: name: app-config - secretRef: name: app-secrets

Common Configuration Mistakes (And How to Avoid Them)

Mistake 1: Hardcoding Passwords

// WRONG ❌ private static final String DB_PASSWORD = "admin123"; // RIGHT ✅ @Value("${spring.datasource.password}") private String dbPassword;

Mistake 2: Using application.properties for Complex Nesting

# WRONG ❌ - Hard to read and maintain spring.datasource.hikari.connection-timeout=20000 spring.datasource.hikari.maximum-pool-size=5 spring.datasource.hikari.idle-timeout=300000 spring.datasource.hikari.max-lifetime=1200000 # RIGHT ✅ - Use application.yml spring: datasource: hikari: connection-timeout: 20000 maximum-pool-size: 5 idle-timeout: 300000 max-lifetime: 1200000

Mistake 3: Forgetting Profile-Specific Overrides

# application.yml (shared) spring: jpa: show-sql: false # application-dev.yml (dev override) spring: jpa: show-sql: true # ✅ Correctly overrides shared setting

Mistake 4: Not Setting Different Pool Sizes for Different Environments

# application-dev.yml spring: datasource: hikari: maximum-pool-size: 5 # Small for development # application-prod.yml spring: datasource: hikari: maximum-pool-size: 20 # Large for production

Without this, production uses development settings = database connection exhaustion.

Mistake 5: Logging Secrets

# WRONG ❌ spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: admin # Logged in application logs password: secret123 # EXPOSED in logs! logging: level: org.hibernate.SQL: DEBUG # Logs all SQL = passwords visible # RIGHT ✅ spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: ${DB_USERNAME} # Not logged password: ${DB_PASSWORD} # Not logged logging: level: org.hibernate.SQL: INFO # Only show errors

Advanced: ConfigurationProperties for Type-Safe Configuration

Instead of scattering @Value annotations everywhere, use @ConfigurationProperties for better organization:

@Configuration @ConfigurationProperties(prefix = "app") public class AppConfig { private String name; private Database database; private Cache cache; public static class Database { private String url; private int poolSize; private int timeout; // getters/setters } public static class Cache { private int ttl; private int maxSize; // getters/setters } // getters/setters }

Then in application.yml:

app: name: MyApp database: url: jdbc:mysql://localhost:3306/mydb poolSize: 10 timeout: 5000 cache: ttl: 3600 maxSize: 1000

Access in your code:

@Service public class MyService { private final AppConfig config; public MyService(AppConfig config) { this.config = config; } public void init() { System.out.println("Pool size: " + config.getDatabase().getPoolSize()); } }

Quick Reference: Common Spring Boot Properties

Server Configuration

server: port: 8080 servlet: context-path: /api ssl: enabled: true key-store: classpath:keystore.jks key-store-password: secret

Database (MySQL)

spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: secret driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # create | create-drop | update | validate | none show-sql: false properties: hibernate: format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect

Database (PostgreSQL)

spring: datasource: url: jdbc:postgresql://localhost:5432/mydb username: postgres password: secret driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.PostgreSQL10Dialect

Logging

logging: level: root: INFO com.myapp: DEBUG org.springframework.web: WARN org.hibernate: WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: name: logs/application.log

Connection Pool (HikariCP)

spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 20000 # 20 seconds idle-timeout: 300000 # 5 minutes max-lifetime: 1200000 # 20 minutes

Validate Your Configuration

Use @ConfigurationProperties with @Validated:

@Configuration @ConfigurationProperties(prefix = "app") @Validated public class AppConfig { @NotBlank(message = "App name cannot be blank") private String name; @Min(1) @Max(65535) private int port; @Email private String adminEmail; // getters/setters }

Spring Boot will fail fast on startup if configuration is invalid:

***************************
APPLICATION FAILED TO START
***************************
 
Field adminEmail in com.myapp.config.AppConfig is invalid:
Value 'admin@invalid' is not a valid email address

Performance Tip: Profile-Specific Datasource Pooling

This single change can improve database performance 10-50x:

# application-dev.yml spring: datasource: hikari: maximum-pool-size: 5 # Small for dev # application-prod.yml spring: datasource: hikari: maximum-pool-size: 20 # Large for prod minimum-idle: 5 connection-timeout: 20000 idle-timeout: 300000

Without this tuning, your production database will be connection-starved.


Detect Configuration Issues with JOptimize

Configuration mistakes often lead to performance and security problems:

npm install -g @joptimize/cli joptimize auth YOUR_API_KEY joptimize analyze .

JOptimize detects:

  • ✓ Hardcoded secrets in code
  • ✓ Missing profile-specific configurations
  • ✓ Insecure database pool settings
  • ✓ Logging configurations that expose secrets
  • ✓ Performance anti-patterns

Key Takeaways

  1. Use application.yml for new projects (more readable than .properties)
  2. Create profile-specific files (dev, staging, prod)
  3. Externalize secrets using environment variables
  4. Tune HikariCP connection pool per environment
  5. Use @ConfigurationProperties for type-safe configuration
  6. Validate configuration on startup
  7. Never log secrets (check your logging levels)

Next Steps

  1. Audit your current application.properties files
  2. Convert to application.yml if using .properties
  3. Create profile-specific configurations
  4. Move secrets to environment variables
  5. Validate configuration with @Validated Get your free code audit:
npm install -g @joptimize/cli joptimize auth YOUR_API_KEY joptimize analyze .

Want to go deeper?

Master Spring Boot, security, and Java performance with hands-on courses.

Detect issues in your project

JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.