Avoid Common SpringBoot Pitfalls: Optimize Default Settings for Production

This article examines the hidden dangers of SpringBoot's out‑of‑the‑box defaults—including Tomcat and HikariCP connection pools, JPA lazy loading, Jackson timezone handling, logging, caching, actuator exposure, file upload limits, async thread pools, static resource caching, and transaction timeouts—and provides concrete configuration tweaks to prevent production failures and boost performance.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Avoid Common SpringBoot Pitfalls: Optimize Default Settings for Production

Tomcat Connection Pool

SpringBoot uses Tomcat as the default web container, but its default connection pool settings (max connections 200, max threads 200) are insufficient for high concurrency, causing request queuing.

server:
  tomcat:
    max-connections: 10000 # maximum connections
    threads:
      max: 800 # maximum worker threads
    min-spare: 100 # minimum idle threads
    accept-count: 100 # waiting queue length
    connection-timeout: 20000

Moreover, the default timeout is unlimited, leaving connections open indefinitely.

Database Connection Pool (HikariCP)

SpringBoot defaults to HikariCP with a maximum pool size of 10, which is far too low for production workloads.

spring:
  datasource:
    hikari:
      maximum-pool-size: 50
      minimum-idle: 10
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 60000

Enable leak detection and increase the pool size to match traffic.

JPA Lazy Loading

Lazy loading often triggers N+1 query problems; use @EntityGraph or JOIN FETCH to load associations eagerly.

@Entity
public class User {
  @Id
  private Long id;
  @OneToMany(fetch = FetchType.LAZY)
  private List<Order> orders;
}

@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();

Jackson Timezone Serialization

Jackson defaults to the system timezone, which can produce inconsistent timestamps in distributed deployments.

spring:
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
    serialization:
      write-dates-as-timestamps: false

Logging Configuration

Logback’s default configuration lacks file rotation and size limits, leading to ever‑growing log files.

logging:
  file:
    name: app.log
  logback:
    rollingpolicy:
      max-file-size: 100MB
      max-history: 30
      total-size-cap: 3GB

Adjust log levels to reduce unnecessary output.

Cache Configuration

The default @Cacheable implementation uses ConcurrentHashMap without expiration, risking memory overflow; switch to Caffeine for better control.

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=10000,expireAfterWrite=600s

Actuator Endpoints

Expose only the endpoints you need and secure them to avoid leaking sensitive information.

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: when-authorized

File Upload Size Limits

SpringBoot’s default limits (1 MB per file, 10 MB total request) are often too low; increase them to suit your use case.

spring:
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
      file-size-threshold: 2KB
      location: /tmp
      resolve-lazily: false

Async Thread Pool

@Async defaults to SimpleAsyncTaskExecutor, which creates a new thread for each task; configure a bounded thread pool instead.

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-

Static Resource Caching

SpringBoot does not set HTTP cache headers for static assets by default; enable caching to avoid re‑downloading 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/

Database Transaction Timeout

Long‑running transactions hold locks and can block other operations; set a timeout and split large batches into smaller transactions.

@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);
    }
}

By reviewing and adjusting these default settings, developers can prevent many production incidents and significantly improve application performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendoptimization
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.