Avoid Hidden SpringBoot Pitfalls: Optimize Default Configurations for Production
This article reveals how SpringBoot's out‑of‑the‑box defaults—such as Tomcat connection limits, HikariCP pool size, JPA lazy loading, Jackson timezone handling, logging, file upload limits, async thread pools, static resource caching, and transaction timeouts—can cause performance bottlenecks and stability issues in production, and provides concrete configuration recommendations to prevent them.
Introduction
When SpringBoot first emerged, its "convention over configuration" promise attracted many developers, but the hidden defaults often become performance traps in real‑world deployments.
Tomcat Connection Pool
SpringBoot uses Tomcat as the default web container, but the default maximum connections (200) and threads (200) are insufficient for high‑concurrency traffic.
server:
tomcat:
max-connections: 10000 # maximum connections
threads:
max: 800 # maximum worker threads
min-spare: 100 # minimum idle threads
accept-count: 100 # request queue length
connection-timeout: 20000 # msThe default timeout is unlimited, causing connections to linger and exhaust resources.
Database Connection Pool
SpringBoot defaults to HikariCP with a maximum pool size of 10, which is too low for most production workloads.
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000Enabling leak-detection-threshold helps detect connection leaks.
JPA Lazy Loading
SpringBoot enables lazy loading by default, which can cause N+1 query problems.
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
}To avoid N+1 queries, use @EntityGraph or JOIN FETCH in repository methods:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();Jackson Timezone Serialization
Jackson defaults to the system timezone, leading to inconsistent timestamps across servers.
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
serialization:
write-dates-as-timestamps: falseExplicit timezone configuration prevents serialization mismatches in distributed environments.
Log Configuration
Logback’s default settings lack file rotation and size limits, causing log files to grow indefinitely.
logging:
file:
name: app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GBAdjust log levels to reduce noise in production.
Cache Configuration
The default @Cacheable implementation uses ConcurrentHashMap without expiration, risking memory overflow.
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=10000,expireAfterWrite=600sCaffeine provides better performance and eviction policies.
Monitoring Endpoints
SpringBoot Actuator exposes many endpoints that may leak sensitive information.
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when-authorizedExpose only necessary endpoints and secure them.
File Upload Size Limits
The default limits (1 MB per file, 10 MB total) are often too low for real applications.
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
file-size-threshold: 2KB
location: /tmp
resolve-lazily: falseAdjust file-size-threshold to balance memory and disk usage.
Async Thread Pool Configuration
Using @Async defaults to SimpleAsyncTaskExecutor, which creates a new thread per task.
spring:
task:
execution:
pool:
core-size: 8
max-size: 16
queue-capacity: 100
keep-alive: 60s
thread-name-prefix: async-task-
scheduling:
pool:
size: 4
thread-name-prefix: scheduling-Configure core and max sizes based on CPU‑bound or IO‑bound workloads.
Static Resource Caching
SpringBoot does not set HTTP cache headers for static assets, causing browsers to re‑download unchanged files.
spring:
web:
resources:
cache:
cachecontrol:
max-age: 365d
cache-public: true
chain:
strategy:
content:
enabled: true
paths: /**
cache: true
static-locations: classpath:/static/Enabling content versioning (e.g., style-abc123.css) allows browsers to cache assets effectively.
Database Transaction Timeout
Without a timeout, long‑running transactions hold locks and can block other operations.
@Transactional(timeout = 30, rollbackFor = Exception.class)
public void batchProcess(List<Data> dataList) {
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
processBatch(batch);
}
}Splitting large jobs into smaller transactions releases locks sooner.
Conclusion
SpringBoot’s conveniences hide many assumptions; proactively tuning defaults—connection pools, caching, logging, serialization, and thread management—prevents production incidents and improves system reliability.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
