Scaling Spring Boot : Real-World Lessons
Hey dev’s ! 👋 After spending three years building and maintaining Spring Boot monolithic services that handle significant traffic, I wanted to share some practical lessons I’ve learned. These aren’t just theoretical concepts — they’re battle-tested solutions that have helped our services perform reliably in production.
Why I’m Writing This
When I first started working with Spring Boot, I struggled to find practical advice about handling high traffic. Most articles were either too theoretical or jumped straight into reactive programming and microservices. But here’s the thing — a well-configured monolithic Spring Boot application can handle substantial load just fine ! Let me share what’s worked for me.
1. Server Configuration : The Foundation
The first production issue I encountered was our application throwing timeout errors under load testing . Here’s what configuration fixed it :
server.tomcat.max-threads=500
server.tomcat.max-connections=10000
server.connection-timeout=120000
Pro tip : update configuration based on your requirements & load .
2. Database : The Most Common Bottleneck
In my experience, database issues cause 80% of performance problems. Here’s what I’ve learned:
Connection Pool Settings
# HikariCP settings
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=250
spring.datasource.hikari.idle-timeout=20000
spring.datasource.hikari.pool-name=MyApp-Datasource
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=20000
Pro tip: I learned the hard way that bigger pool sizes aren’t always better. Start small and increase only if needed.
One more thing important thing is to optimized queries .
The Magic of Proper Indexing
-- example
-- Before
SELECT * FROM orders WHERE user_id = ? AND status = 'ACTIVE' ORDER BY created_at DESC;
-- After adding this index, same query takes 100ms
CREATE INDEX idx_order_lookup ON orders(user_id, status, created_at);
3. Caching: Your Best Friend
you can use Spring’s default cache or third party caching such as redis
@Cacheable(value = "products", key = "#id")
public Product getProduct(Long id) {
return productRepository.findById(id).orElse(null);
}
But see if you want to plan to scalabality then go for redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.timeout=2000
Quick tip: Cache invalidation is tricky. I prefer short TTLs over complex invalidation logic.
4. Large Data Processing
One of our endpoints was timing out when handling longer processing requests . Spring batch processing approach will solved that issue like bulk operations.
5. JVM Settings That Actually Matter
To make your Java or Spring Boot application faster, follow JVM tuning tips. First, optimize heap memory by setting initial and maximum sizes to avoid runtime resizing issues. Use the G1 Garbage Collector for low-latency performance and enable garbage collection logs for debugging. Tools like GraalVM can improve startup time with AOT compilation, while Class Data Sharing reduces overhead during class loading. For containerized environments, adjust container-specific settings to effectively utilize resources. Lastly, continuous monitoring with profiling tools ensures optimal performance.
# Heap Memory Optimization
-Xms512m -Xmx1024m
# Use G1 Garbage Collector
-XX:+UseG1GC
# Enable Garbage Collection Logs
-Xlog:gc*,gc+cpu,gc+heap=info
# Ahead-of-Time Compilation (GraalVM)
native-image -jar app.jar
# Container-Specific Settings
-XX:+UseContainerSupport
-XX:MaxRAMPercentage=75
# Disable Biased Locking (For multi-threaded apps)
-XX:-UseBiasedLocking
# Enable Class Data Sharing
-Xshare:on
# Thread Pool Optimization
-XX:ParallelGCThreads=4 -XX:ConcGCThreads=2
6. REST API Design Lessons
The biggest lesson? Keep endpoints focused. Here’s example :
// Before: One endpoint doing too much
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
// 200 lines of business logic, email sending, inventory updates...
}
// After: Focused endpoints
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
Order order = orderService.createOrder(request); // Core logic only
eventPublisher.publishEvent(new OrderCreatedEvent(order)); // Let others handle side effects
return order;
}
Quick tip: Don’t forget to add Spring Actuator in you service to monitor metrics
You don’t need fancy architectures or reactive programming to handle high traffic. A well-configured Spring Boot monolith can take you far. Start with these basics, measure everything, and optimize based on real data.
Remember:
- Monitor before optimizing
- Keep it simple
- Test with production-like data
- Small, incremental improvements > big rewrites
Hope this helps with your scaling !
Follow for more , Thanks !