Spring Boot config doctor
Paste your application.yml or application.properties. The doctor finds duplicate keys, renamed Spring Boot 3 properties, exposed actuator endpoints, plaintext secrets and connection pool mistakes, and tells you exactly what to change.
Everything runs in your browser. Nothing you paste is uploaded. Also try the paste-anything box or the AI-output verifier.
What the Spring config doctor checks
Spring Boot is forgiving about configuration, and that is the problem: a misspelled or renamed key is silently ignored, and the application starts with a default you never chose. This is how a Redis host set under the old key leaves a service talking to localhost after an upgrade, and how a wildcard actuator setting ends up publishing heap dumps. The rules below come from upgrade notes, security advisories and common production incidents.
There are 40 rules in this doctor: 13 high severity, 21 medium and 6 low. Each rule has a stable anchor, so you can link a teammate straight to the explanation, for example /doctor/spring-config/#rule-duplicate-key.
All 40 rules
HighDuplicate key#
Why it matters. The same key appears twice. In YAML the parser errors or the last one silently wins; in a properties file the last one wins. One of the two values is dead and the app runs with a value you may not expect.
Fix. Delete one of the two entries, or merge them under a single parent key. Properties to YAML
server.port=1 server.port=2
MediumEnvironment-variable style key in a config file#
Why it matters. SPRING_DATASOURCE_URL style names only bind from real environment variables. Inside application.properties or YAML they are not matched to spring.datasource.url.
Fix. Use the canonical kebab-case key (spring.datasource.url) in the file and keep the upper-case form for the environment. Spring env converter
SPRING_DATASOURCE_URL=jdbc:h2:mem:t
LowcamelCase or snake_case key#
Why it matters. Relaxed binding lets @ConfigurationProperties accept these, but @Value, @ConditionalOnProperty and some tooling only match the canonical kebab-case form, so the same key works in one place and not another.
Fix. Write the key in kebab-case, for example server.servlet.context-path. Spring env converter
server.servlet.contextPath=/api
Mediumjavax.persistence property under Spring Boot 3#
Why it matters. Jakarta EE 9 renamed the namespace. Boot 3 and Hibernate 6 read jakarta.persistence.*, so a javax.persistence.* property is silently ignored.
Fix. Rename javax.persistence.* to jakarta.persistence.* (and javax.validation to jakarta.validation in code). Java exceptions
spring.jpa.properties.javax.persistence.validation.mode=none
HighActuator exposes every endpoint#
Why it matters. management.endpoints.web.exposure.include=* publishes env, heapdump, threaddump, beans and more over HTTP. Heap dumps contain passwords and tokens.
Fix. Expose only health and info (and metrics or prometheus if you secure them): include=health,info. Spring concepts
management.endpoints.web.exposure.include=*
HighSensitive actuator endpoint exposed#
Why it matters. env, heapdump, threaddump, configprops, shutdown and jolokia leak configuration or let strangers act on the process.
Fix. Remove these from management.endpoints.web.exposure.include, or move management to a private port with authentication.
management.endpoints.web.exposure.include=health,env,heapdump
MediumHealth details shown to everyone#
Why it matters. show-details=always lists database, disk and broker status to anonymous callers, which maps your infrastructure.
Fix. Use when-authorized, or keep always only on a private management port.
management.endpoint.health.show-details=always
Mediummanagement.security.* no longer exists#
Why it matters. These Boot 1 keys were removed in Boot 2. Actuator security now comes from Spring Security, so the line does nothing and gives false comfort.
Fix. Delete the key and secure /actuator/** with a SecurityFilterChain.
management.security.enabled=false
MediumRenamed or removed property, use spring.data.redis.*#
Why it matters. Spring Boot 3 moved the Redis properties under spring.data.redis, so the old keys are ignored and Redis silently connects to localhost.
Fix. Rename the key to spring.data.redis.*. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.redis.host=cache
MediumRenamed or removed property, use spring.elasticsearch.*#
Why it matters. The REST client properties lost the rest segment in Boot 3.
Fix. Rename the key to spring.elasticsearch.*. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.elasticsearch.rest.uris=http://es:9200
MediumRenamed or removed property, use spring.cassandra.*#
Why it matters. Cassandra settings moved out of spring.data in Boot 3.
Fix. Rename the key to spring.cassandra.*. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.data.cassandra.contact-points=c1
MediumRenamed or removed property, use spring.web.resources.*#
Why it matters. spring.resources.* was renamed in Boot 2.4 and removed in 3.0.
Fix. Rename the key to spring.web.resources.*. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.resources.static-locations=classpath:/x/
MediumRenamed or removed property, use spring.sql.init.mode#
Why it matters. The datasource initialisation properties moved to spring.sql.init in Boot 2.5.
Fix. Rename the key to spring.sql.init.mode. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.datasource.initialization-mode=always
MediumRenamed or removed property, use spring.sql.init.schema-locations or data-locations#
Why it matters. The schema and data script properties moved to spring.sql.init.
Fix. Rename the key to spring.sql.init.schema-locations or data-locations. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.datasource.schema=classpath:schema.sql
MediumRenamed or removed property, use server.forward-headers-strategy#
Why it matters. The boolean was replaced by a strategy (native, framework, none).
Fix. Rename the key to server.forward-headers-strategy. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
server.use-forward-headers=true
MediumRenamed or removed property, use server.max-http-request-header-size#
Why it matters. Renamed in Boot 2.1 and removed in 3.0.
Fix. Rename the key to server.max-http-request-header-size. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
server.max-http-header-size=16KB
MediumRenamed or removed property, use nothing (remove it)#
Why it matters. Hibernate 6 always uses the new id generators, so the property no longer exists.
Fix. Rename the key to nothing (remove it). Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.jpa.hibernate.use-new-id-generator-mappings=true
MediumRenamed or removed property, use management.<registry>.metrics.export.*#
Why it matters. Boot 3 moved metrics export settings, for example management.prometheus.metrics.export.enabled.
Fix. Rename the key to management.<registry>.metrics.export.*. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
management.metrics.export.prometheus.enabled=true
MediumRenamed or removed property, use spring.mvc.problemdetails.enabled and NoResourceFoundException handling#
Why it matters. Boot 3.2 serves static resources through a resource handler that throws NoResourceFoundException, so this flag no longer behaves as before.
Fix. Rename the key to spring.mvc.problemdetails.enabled and NoResourceFoundException handling. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.mvc.throw-exception-if-no-handler-found=true
MediumRenamed or removed property, use assertingparty#
Why it matters. The identityprovider segment became assertingparty in Boot 3.
Fix. Rename the key to assertingparty. Add spring-boot-properties-migrator to the classpath for one run and it lists every rename it finds. Spring env converter
spring.security.saml2.relyingparty.registration.x.identityprovider.entity-id=a
HighPlaintext secret in config#
Why it matters. A literal password, token or key in a file ends up in git history, build artifacts and logs.
Fix. Reference an environment variable (${DB_PASSWORD}), a Kubernetes secret, Vault or a Jasypt ENC(...) value instead. Password generator
spring.datasource.password=hunter2
HighCredentials inside a connection URL#
Why it matters. user:password@host or password= in a URL is logged by drivers, shown in actuator output and copied into tickets.
Fix. Move the credentials to the separate username and password properties, fed from the environment. JDBC URL builder
spring.datasource.url=jdbc:postgresql://db/app?user=app&password=secret
MediumConnection pool maximum looks too large#
Why it matters. More than 50 connections per instance rarely helps; the database spends its time context switching and ten instances can exhaust max_connections.
Fix. Start near 10 and size with the formula in the HikariCP guide, multiplied by instance count. HikariCP pool sizing
spring.datasource.hikari.maximum-pool-size=200
Highminimum-idle larger than maximum-pool-size#
Why it matters. HikariCP caps the idle count at the maximum, so the setting is misleading and usually means the two values were mixed up.
Fix. Make minimum-idle less than or equal to maximum-pool-size, or drop it so Hikari uses a fixed-size pool. HikariCP pool sizing
spring.datasource.hikari.minimum-idle=30 spring.datasource.hikari.maximum-pool-size=10
MediumConnection pool of one#
Why it matters. A pool of one deadlocks as soon as a transaction needs a second connection, for example REQUIRES_NEW or a nested repository call.
Fix. Use at least 5, and enable leak-detection-threshold while you investigate. HikariCP pool sizing
spring.datasource.hikari.maximum-pool-size=1
HighHibernate ddl-auto can change or drop tables#
Why it matters. create and create-drop wipe data on every start; update applies unreviewed schema changes and never removes anything.
Fix. Use none or validate, and manage the schema with Flyway or Liquibase migrations.
spring.jpa.hibernate.ddl-auto=create-drop
Lowopen-in-view is left on#
Why it matters. Spring Boot keeps a database connection open for the whole web request by default and logs a warning at startup. Lazy loading in controllers hides N+1 queries.
Fix. Set spring.jpa.open-in-view=false and fetch what the view needs in the service. Learn JPA
spring.jpa.hibernate.ddl-auto=validate
Lowshow-sql prints to stdout#
Why it matters. spring.jpa.show-sql writes unformatted SQL to the console without parameters and bypasses your log levels.
Fix. Use logging.level.org.hibernate.SQL=DEBUG for the statements and BasicBinder at TRACE for values, in dev only.
spring.jpa.show-sql=true
MediumDebug logging switched on globally#
Why it matters. logging.level.root=DEBUG or debug=true floods logs, slows the app and can write personal data and tokens.
Fix. Keep root at INFO and raise only the package you are investigating.
logging.level.root=DEBUG
Highspring.profiles.active inside a profile-specific section#
Why it matters. Since Spring Boot 2.4 a document guarded by spring.config.activate.on-profile cannot set spring.profiles.active; startup fails with InvalidConfigDataPropertyException.
Fix. Set the active profile in the first (default) document, an environment variable or the command line.
spring:
config:
activate:
on-profile: prod
profiles:
active: xMediumLegacy spring.profiles key#
Why it matters. spring.profiles: dev as a document selector was replaced by spring.config.activate.on-profile in Boot 2.4 and fails in Boot 3.
Fix. Replace it with spring.config.activate.on-profile (and spring.profiles.include with spring.profiles.group).
spring.profiles=dev
HighH2 console enabled#
Why it matters. The H2 web console allows arbitrary SQL, and with remote access enabled it is a well known route to remote code execution.
Fix. Turn spring.h2.console.enabled off outside local development and never allow web-allow-others.
spring.h2.console.enabled=true
HighStack traces or messages returned to clients#
Why it matters. server.error.include-stacktrace=always (or include-message / include-binding-errors) leaks class names, SQL and paths to anyone who can trigger an error.
Fix. Use never (the default) in production and log the details server-side.
server.error.include-stacktrace=always
HighFlyway clean is allowed#
Why it matters. spring.flyway.clean-disabled=false lets a stray flyway.clean() or a mis-set property erase the whole schema.
Fix. Leave clean-disabled at true (the default) everywhere except a throwaway test database.
spring.flyway.clean-disabled=false
LowTLS explicitly disabled#
Why it matters. server.ssl.enabled=false is fine behind a TLS terminating proxy, but it is a problem if the port is reachable directly.
Fix. Make sure the proxy forwards X-Forwarded-Proto and set server.forward-headers-strategy=framework.
server.ssl.enabled=false
LowVery large Tomcat thread pool#
Why it matters. Thousands of platform threads use stack memory and make contention worse; the bottleneck is usually the database pool.
Fix. Keep it near the default of 200, or use virtual threads (spring.threads.virtual.enabled=true) on Java 21.
server.tomcat.threads.max=5000
HighDevTools remote secret set#
Why it matters. spring.devtools.remote.secret enables remote restart and code update endpoints, which must never ship to production.
Fix. Remove it, and exclude spring-boot-devtools from the production build.
spring.devtools.remote.secret=abc
MediumWildcard origins in configuration#
Why it matters. An allowed-origins value of * lets any site call your API from a browser, and browsers reject it when credentials are allowed.
Fix. List the exact origins, per environment. CORS config builder
cors.allowed-origins=*
LowProperty with an empty value#
Why it matters. An empty value overrides the default with an empty string, which often disables a feature or breaks parsing.
Fix. Remove the line, or use ${VAR:default} so there is always a value. Properties to YAML
spring.application.name=
HighThe YAML does not parse#
Why it matters. Spring Boot fails at startup with a ScannerException or ParserException on this file.
Fix. Fix the indentation or quoting at the reported line. Use spaces, not tabs. YAML to JSON
a: b: 1 c: 2
Frequently asked questions
Does it understand both YAML and .properties files?
Yes. The doctor detects the format, flattens nested YAML into dotted keys, and reports line numbers for either one.
Which Spring Boot version do the renames target?
The rename rules cover the move from Spring Boot 2.x to 3.x, such as spring.redis becoming spring.data.redis and the metrics export keys moving under each registry. Adding spring-boot-properties-migrator for one run lists any rename not in this table.
Is my configuration uploaded?
No. The rules run in your browser, so passwords in the file never leave your machine. Even so, replace real secrets with placeholders before sharing a report.
Why is a kebab-case key preferred?
Relaxed binding accepts camelCase and snake_case in @ConfigurationProperties, but @Value and @ConditionalOnProperty match the canonical kebab-case name only, so mixed styles behave differently in different places.