You've Built It — Now It's Time to Scale It
You've explored the internals of Spring Boot, handled async operations, used custom filters, and even spun up Docker containers in your tests.
But real-world software doesn't stop at writing code. It lives in the cloud, runs under heavy load, gets monitored, logged, deployed hundreds of times, and needs to recover from failure.
Welcome to Part 3 of this deep-dive series where we move beyond development and into deploying, scaling, monitoring, and securing Spring Boot applications in production.
If you've ever asked:
- How do I deploy Spring Boot the right way?
- What's the best logging strategy in microservices?
- How do I monitor and alert on failures?
- How can I optimize performance in real time?
This guide is for you.
1. Spring Boot in the Cloud — Stateless, Scalable, Deployable
Spring Boot was born for the cloud — but you need to prepare it for the cloud properly.
Rule #1: Make Your App Stateless
Avoid storing user sessions or application state in memory. Use:
- Redis / Memcached for session data
- Databases or file stores (like S3) for persistent data
- Spring Cloud Config for dynamic configuration
server.servlet.session.persistent=true
spring.session.store-type=redisRule #2: Externalize Everything
Your application should never rely on local .properties in production.
Use:
- Environment variables
- Spring Cloud Config
- Kubernetes ConfigMaps / Secrets
Rule #3: Readiness & Liveness Probes
When deploying on Kubernetes, configure health endpoints:
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=alwaysIn application.yaml:
management:
health:
probes:
enabled: true2. CI/CD with Spring Boot — The Automation You Can't Ignore
Manual deployments break things. Let's automate!
Step-by-Step CI/CD with GitHub Actions (Example)
.github/workflows/deploy.yml
name: Build and Deploy Spring Boot
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Docker Build
run: docker build -t myapp:latest .
- name: Push to DockerHub
run: |
docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
docker tag myapp:latest mydockeruser/myapp:latest
docker push mydockeruser/myapp:latestDeploy to:
- Kubernetes (via Helm/Manifests)
- AWS ECS / EKS
- Google Cloud Run / App Engine
3. Observability: Tracing, Metrics & Logs
You can't fix what you can't see.
A. Metrics with Micrometer + Prometheus + Grafana
Spring Boot uses Micrometer under the hood.
management.endpoints.web.exposure.include=*
management.metrics.export.prometheus.enabled=trueAccess metrics at /actuator/prometheus.
Visualize with Grafana, and you get dashboards like:
- JVM memory usage
- HTTP request latency
- DB connections
- Custom business metrics
Custom Metric Example:
@Autowired
MeterRegistry registry;
@PostConstruct
public void init() {
registry.counter("my_custom_counter").increment();
}B. Distributed Tracing with Spring Cloud Sleuth + Zipkin
Add dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>This will:
- Auto-tag logs with trace & span IDs
- Send traces to Zipkin or OpenTelemetry backend
C. Centralized Logging with ELK or Loki
Avoid logging to local files. Stream logs to:
- Elastic Stack (ELK)
- Grafana Loki
- AWS CloudWatch Logs
Recommended logging format:
logging.pattern.level=%5p [${spring.application.name:},%X{X-B3-TraceId}]Or use JSON structured logging for better parsing.
4. Circuit Breakers with Spring Cloud Resilience4j
To prevent cascading failures when external services go down, use Circuit Breakers.
Add dependency:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>Then use:
@CircuitBreaker(name = "paymentService", fallbackMethod = "fallback")
public String callExternalAPI() {
return restTemplate.getForObject("/api", String.class);
}
public String fallback(Exception e) {
return "Default response";
}You can combine this with:
- Retries
- Rate Limiters
- Bulkheads
Resilience4j dashboard also available via actuator endpoints.
5. Securing APIs: OAuth2 + Rate Limiting + CORS
OAuth2 with Spring Security
Add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>Configure JWT:
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://auth.example.com/Rate Limiting with Bucket4j or Redis
@Bucket(name = "apiBucket", capacity = 100, refillTokens = 10, refillPeriod = Duration.ofSeconds(1))
public ResponseEntity<?> getProducts() {
// logic
}Protects from abusive clients and DDoS-style attacks.
CORS and Security Headers
@Override
protected void configure(HttpSecurity http) {
http.cors().and().headers()
.xssProtection().and()
.frameOptions().deny();
}6. Optimize Performance for Real-World Traffic
Enable HTTP/2
Just add this in application.properties:
server.http2.enabled=trueUse Connection Pooling with HikariCP
Already default in Spring Boot. But tune it:
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000Enable Response Compression
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html7. Graceful Rollbacks and Feature Toggles (Advanced Deployment Strategy)
Use LaunchDarkly, FF4J, or a home-grown solution to control features at runtime.
if (featureManager.isActive("new_checkout_flow")) {
return newFlow();
} else {
return oldFlow();
}This allows safe releases, A/B testing, and rollback without redeploying.
Read my other articles :
Thank You for Reading!
✅ "Follow me on Medium to never miss an update!"
If you found this content helpful, feel free to show your support with 👏 claps! 😊 Your encouragement keeps me motivated to share more insights