Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Home About
Products
Services
Hire
Industries
Consulting
Partners
Articles Careers Contact
Software Development

Spring Boot Microservices: Complete Guide 2026

Service discovery, API gateway, circuit breakers, distributed tracing, and Spring Cloud microservices best practices — with production-ready code examples.

Updated April 12, 2026 20 min read
In this article

Spring Boot microservices remain the dominant architecture for Java backends in 2026. Building microservices with Spring Boot gives you a mature, opinionated foundation — autoconfigured HTTP servers, production-ready health checks, and the entire Spring Cloud ecosystem for the hard distributed systems problems: service discovery, configuration management, circuit breakers, and distributed tracing. This guide covers every layer of a production Spring Boot microservices architecture, with code you can use today.

At Pillai Infotech, we build and staff Java microservices teams for banking, insurance, and large-scale B2B platforms. What follows is the reference we give our own Java developers when they start a new microservices project.

What Is Spring Boot Microservices Architecture?

A Spring Boot microservices architecture decomposes a system into small, independently deployable services, each owning its own data store and exposing a well-defined API. Spring Boot provides the runtime for each service; Spring Cloud provides the infrastructure glue between them.

The difference between Spring Boot and Spring Cloud is worth stating clearly: Spring Boot is the application framework — embedded Tomcat, autoconfiguration, starters. Spring Cloud is the distributed systems toolkit — Eureka for service discovery, Spring Cloud Gateway for routing, Resilience4j for circuit breaking, Micrometer for observability. You use both together.

Concern Spring Cloud Solution Kubernetes-native Alternative
Service DiscoveryEureka / ConsulKubernetes DNS
Config ManagementSpring Cloud ConfigConfigMaps / Vault
Circuit BreakerResilience4jIstio service mesh
API GatewaySpring Cloud GatewayKong / Nginx Ingress
Distributed TracingMicrometer Tracing + OTLPOpenTelemetry Collector
Event StreamingSpring Cloud Stream + KafkaKafka client directly
2026 architecture decision: If you are deploying on Kubernetes, skip Eureka and Spring Cloud Config — K8s provides service discovery via DNS and ConfigMaps natively. Spring Cloud components add the most value when you are not on K8s, or when you need client-side load balancing and fine-grained circuit-breaker control at the application layer.

If you need help designing the right microservices architecture for your system, see our technology roadmap consulting service — we help engineering leads choose the right stack before a line of code is written.

Service Structure and Project Layout

Every Spring Boot microservice should be independently buildable, testable, and deployable. Keep each service's scope narrow — one bounded context, one database schema, one deployment unit. Here is the standard layout we use across all Spring Boot microservices projects:

order-service/
├── src/main/java/com/example/orders/
│   ├── OrderServiceApplication.java
│   ├── controller/
│   │   └── OrderController.java       // REST endpoints
│   ├── service/
│   │   └── OrderService.java          // Business logic
│   ├── repository/
│   │   └── OrderRepository.java       // Data access (JPA)
│   ├── model/
│   │   ├── Order.java                 // Entity
│   │   └── OrderDTO.java              // Transfer object
│   ├── client/
│   │   └── InventoryClient.java       // Feign client to another service
│   ├── config/
│   │   └── SecurityConfig.java
│   └── exception/
│       └── GlobalExceptionHandler.java
├── src/main/resources/
│   ├── application.yml
│   └── application-prod.yml
├── Dockerfile
└── pom.xml

The client/ package holds all outbound Feign clients — it is the only place your service calls other services. This keeps inter-service communication explicit and auditable. The exception/ package contains a single @RestControllerAdvice that maps domain exceptions to HTTP status codes uniformly.

// OrderController.java
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public OrderDTO createOrder(@Valid @RequestBody CreateOrderRequest request) {
        return orderService.createOrder(request);
    }

    @GetMapping("/{id}")
    public OrderDTO getOrder(@PathVariable Long id) {
        return orderService.getOrder(id);
    }

    @GetMapping
    public Page<OrderDTO> listOrders(Pageable pageable) {
        return orderService.listOrders(pageable);
    }
}

Note the use of @Valid on request bodies — every inbound payload is validated at the controller layer before reaching service logic. Never trust data from other services without validation; in a microservices architecture, every service is a public API boundary.

How Do You Set Up Service Discovery in Spring Boot?

Service discovery in Spring Boot microservices solves the problem of services finding each other dynamically as instances scale up and down. There are two mainstream approaches: Eureka (Spring Cloud's built-in registry) and Kubernetes DNS (if you are already on K8s).

Option 1 — Eureka (non-K8s environments):

# application.yml — Eureka client
spring:
  application:
    name: order-service
eureka:
  client:
    service-url:
      defaultZone: http://eureka-server:8761/eureka/
  instance:
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 10
    lease-expiration-duration-in-seconds: 30

Option 2 — Kubernetes DNS (recommended on K8s):

# application.yml — Kubernetes-native discovery
spring:
  cloud:
    kubernetes:
      discovery:
        enabled: true
      config:
        enabled: true   # reads from ConfigMaps

With either option, OpenFeign lets you call other services by name — Spring resolves the actual hostname at runtime:

// Feign client — calls inventory-service by logical name
@FeignClient(name = "inventory-service")
public interface InventoryClient {
    @GetMapping("/api/inventory/{productId}")
    InventoryResponse checkStock(@PathVariable String productId);
}
// Spring resolves "inventory-service" via Eureka or K8s DNS at runtime
Practical advice: If you are deploying to bare-metal or VM clusters without Kubernetes, Eureka is the right choice. If you are on EKS, GKE, or AKS, skip Eureka entirely — you are paying for K8s service discovery already. Running both is operational waste.

How Do Microservices Communicate in Spring Boot?

Spring Boot microservices communication falls into two categories: synchronous (the caller waits for a response) and asynchronous (the caller publishes an event and moves on). Choosing the wrong model is one of the most common architectural mistakes in Spring Boot microservices projects.

Pattern Spring Tool Use When
Sync RESTOpenFeign / RestClientCaller needs the response to proceed
Sync gRPCgrpc-spring-boot-starterHigh-throughput, low-latency internal APIs
Async EventsSpring Cloud Stream + KafkaFire-and-forget, event sourcing, fanout
Async CommandsRabbitMQ / ActiveMQTask queues, work distribution

The governing rule: default to async event-driven communication. Use synchronous calls only where the caller genuinely cannot proceed without the response. Every synchronous dependency creates a coupling that can cascade into a full outage when the downstream service slows down. In a Spring Boot microservices architecture built around Kafka, a single slow consumer does not stall the publisher.

// Event-driven with Spring Cloud Stream + Kafka
@Service
@RequiredArgsConstructor
public class OrderService {
    private final StreamBridge streamBridge;

    public OrderDTO createOrder(CreateOrderRequest request) {
        Order order = orderRepository.save(toEntity(request));
        // Publish event — inventory-service consumes async, no coupling
        streamBridge.send("order-created",
            new OrderCreatedEvent(order.getId(), order.getItems()));
        return toDTO(order);
    }
}

// Consumer in inventory-service — completely decoupled
@Bean
public Consumer<OrderCreatedEvent> orderCreated() {
    return event -> inventoryService.reserveStock(event);
}

For distributed transactions across services, avoid two-phase commit. Use the Saga pattern instead — either choreography (each service reacts to events and publishes the next) or orchestration (a coordinator service drives the sequence). Spring Cloud Stream with Kafka is the standard implementation vehicle for both. See our microservices architecture guide for a full Saga implementation walkthrough.

Resilience Patterns with Resilience4j

No Spring Boot microservices architecture is complete without resilience at the call boundary. Resilience4j is the standard library since Hystrix reached end-of-life. It gives you circuit breakers, retries, rate limiters, and time limiters — all composable via annotations.

@CircuitBreaker(name = "inventory", fallbackMethod = "fallbackStock")
@Retry(name = "inventory", fallbackMethod = "fallbackStock")
@TimeLimiter(name = "inventory")
public CompletableFuture<InventoryResponse> checkStock(String productId) {
    return CompletableFuture.supplyAsync(
        () -> inventoryClient.checkStock(productId));
}

public CompletableFuture<InventoryResponse> fallbackStock(
        String productId, Throwable t) {
    log.warn("Inventory service unavailable for product {}", productId, t);
    return CompletableFuture.completedFuture(
        InventoryResponse.unknown());
}
# application.yml — Resilience4j configuration
resilience4j:
  circuitbreaker:
    instances:
      inventory:
        slidingWindowSize: 10
        failureRateThreshold: 50        # open circuit at 50% failures
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3
  retry:
    instances:
      inventory:
        maxAttempts: 3
        waitDuration: 500ms
        exponentialBackoffMultiplier: 2
        retryExceptions:
          - java.io.IOException
          - feign.RetryableException
  timelimiter:
    instances:
      inventory:
        timeoutDuration: 3s

The annotation order matters: @TimeLimiter wraps @Retry which wraps @CircuitBreaker. A timeout triggers a retry; repeated retries that fail open the circuit. This layered behaviour is intentional — do not reorder the annotations.

What Is the Best API Gateway for Spring Boot Microservices?

Spring Cloud Gateway is the recommended API gateway for Spring Boot microservices in 2026. It is built on Project Reactor (non-blocking), integrates natively with Eureka and Spring Security, and supports reactive rate limiting backed by Redis. If your team is already in the Spring ecosystem, Spring Cloud Gateway has zero onboarding friction.

The main alternative is Kong (for teams that need plugin-driven gateway logic managed outside Java) or Nginx Ingress (for K8s teams who want minimal complexity). For most Spring Boot microservices deployments, Spring Cloud Gateway is the right answer.

# Spring Cloud Gateway — routing + circuit breaker + rate limiting
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - name: CircuitBreaker
              args:
                name: orderCB
                fallbackUri: forward:/fallback/orders
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 50
                redis-rate-limiter.burstCapacity: 100
                redis-rate-limiter.requestedTokens: 1
        - id: inventory-service
          uri: lb://inventory-service
          predicates:
            - Path=/api/inventory/**
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
        - name: Retry
          args:
            retries: 2
            statuses: BAD_GATEWAY, SERVICE_UNAVAILABLE

The gateway is also where you centralise cross-cutting concerns: JWT validation, request logging, CORS, and global rate limiting. Keeping these out of individual services keeps each microservice focused on business logic.

If your project involves building a multi-service backend from scratch, our team provides custom software development services including architecture, implementation, and handoff to your in-house team.

How Do You Monitor Spring Boot Microservices?

Monitoring Spring Boot microservices requires three pillars: metrics (what is happening), traces (where time is being spent across services), and logs (what went wrong and why). Spring Boot 3.x makes all three first-class through Spring Boot Actuator and Micrometer.

Metrics with Prometheus and Grafana:

# application.yml — expose metrics and tracing
management:
  endpoints:
    web:
      exposure:
        include: health, prometheus, info, metrics
  endpoint:
    health:
      show-details: always
  tracing:
    sampling:
      probability: 1.0          # 100% in dev; 0.1 in production
  otlp:
    tracing:
      endpoint: http://otel-collector:4318/v1/traces
  metrics:
    export:
      prometheus:
        enabled: true

Distributed tracing with Micrometer + Jaeger:

// Add to pom.xml
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

// Trace IDs propagate automatically through Feign clients and RestTemplate.
// In logs, configure Logback to include traceId and spanId:
// logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]

In production, set tracing.sampling.probability to 0.1 (10% of requests) or use head-based sampling at the OpenTelemetry Collector. Sampling 100% at scale generates more telemetry data than most teams can store affordably.

The recommended observability stack for Spring Boot microservices in 2026: Prometheus + Grafana for metrics dashboards, Jaeger or Tempo for distributed tracing, and Loki for log aggregation. All three integrate with Grafana, giving you a single pane of glass across your entire microservices deployment.

See our observability guide for the full monitoring stack setup with Docker Compose.

Deployment: Docker and Kubernetes

Deploying Spring Boot microservices with Docker is standard. Use multi-stage builds to keep image sizes small and use a non-root user for security compliance:

# Multi-stage Dockerfile for Spring Boot microservices
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./mvnw package -DskipTests

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=builder /app/target/*.jar app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", \
    "-XX:+UseContainerSupport", \
    "-XX:MaxRAMPercentage=75.0", \
    "-jar", "app.jar"]

The JVM flags are important: -XX:+UseContainerSupport tells the JVM to respect cgroup memory limits (critical in Kubernetes with resource quotas), and -XX:MaxRAMPercentage=75.0 leaves 25% headroom for OS overhead.

Kubernetes deployment for a Spring Boot microservice:

# k8s/order-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: your-registry/order-service:latest
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"

Spring Boot 3.2+ supports GraalVM native images — startup drops from ~2 seconds to ~50ms, memory from ~250MB to ~50MB. This is worth the compilation overhead for serverless or cost-sensitive deployments. For standard long-running Kubernetes pods, the JVM with CDS (Class Data Sharing) is still the more practical choice.

See our Docker containerization guide and CI/CD pipeline guide for deployment automation patterns.

Spring Boot Microservices Best Practices for 2026

These are the decisions that separate Spring Boot microservices that scale cleanly from those that become unmaintainable within 18 months. The Spring Boot microservices best practices that matter most in 2026:

1. Domain-driven service boundaries. Size services by business capability, not by technical layer. An "order-service" is right; a "database-service" is not. Each service owns its schema — no shared databases between services. If two services query the same table, that is a boundary design problem, not a database problem.

2. API versioning from day one. Version your REST APIs under /api/v1/ from the first commit. Changing API contracts without versioning in a microservices architecture breaks all consumers simultaneously.

3. Externalize all configuration. No hard-coded environment-specific values in code. Use Spring Cloud Config or Kubernetes ConfigMaps. The same JAR artifact should run in dev, staging, and production with only configuration differences.

4. Design for failure, not for the happy path. Every synchronous call to another service will eventually fail. Every Feign client needs a fallback. Every operation that modifies external state needs an idempotency key. Use Resilience4j circuit breakers on all synchronous inter-service calls without exception.

5. Structured, correlated logging. Every log entry must include the trace ID and span ID from Micrometer. Without correlation IDs, debugging a failure that spans five services becomes a multi-hour manual trace through log files.

6. Consumer-driven contract testing. Use Pact or Spring Cloud Contract to verify that your Feign clients match what downstream services actually provide. Integration tests against a mock are insufficient — they test what you think the API does, not what it actually does.

7. Start smaller than you think. The most common Spring Boot microservices mistake is over-decomposing too early. A modular monolith with clean package boundaries is easier to extract services from than a poorly-bounded set of 20 microservices with excessive synchronous coupling. Extract a service when you have a concrete deployment-independence reason, not as a default.

Building microservices? Read our guides on scalable microservices architecture, Docker best practices, and when to use serverless.

If your backend team is scaling and you need experienced Spring Cloud engineers, our backend developers are available for both short-term delivery and long-term team augmentation.

Frequently Asked Questions

What is the difference between Spring Boot and Spring Cloud?
Spring Boot is the application framework — embedded Tomcat or Netty, autoconfiguration, production-ready defaults. Spring Cloud is the distributed systems layer built on top of Spring Boot: Eureka for service discovery, Spring Cloud Gateway for API routing, Resilience4j for circuit breaking, and Micrometer for distributed tracing. You use Spring Boot to build each microservice and Spring Cloud to connect them.
How do you handle service-to-service communication in Spring Boot?
Default to asynchronous event-driven communication using Spring Cloud Stream with Kafka or RabbitMQ. Use synchronous REST calls (via OpenFeign) only when the caller genuinely cannot proceed without the immediate response. Every synchronous call must be wrapped in a Resilience4j circuit breaker with a fallback. gRPC is worth evaluating for high-throughput internal APIs where REST overhead is measurable.
What is the best way to deploy Spring Boot microservices?
Docker containers orchestrated by Kubernetes is the production standard for Spring Boot microservices in 2026. Build a multi-stage Dockerfile to keep image sizes under 200MB, use eclipse-temurin:21-jre-alpine as the runtime base, add JVM flags for container memory awareness (-XX:+UseContainerSupport), and configure readiness and liveness probes pointing to Spring Boot Actuator endpoints. For low-traffic or serverless scenarios, GraalVM native images cut startup time to under 100ms.
How do you monitor Spring Boot microservices?
Use Micrometer with Prometheus for metrics, OpenTelemetry via the Micrometer Tracing bridge for distributed traces (export to Jaeger or Grafana Tempo), and structured JSON logs with injected trace and span IDs for correlation. In Spring Boot 3.x, enable Actuator endpoints (health, prometheus), set management.tracing.sampling.probability, and configure the OTLP exporter endpoint. Grafana is the standard dashboard layer for all three data sources.
Do I need Eureka for service discovery if I am on Kubernetes?
No. Kubernetes provides service discovery via DNS natively — each K8s Service gets a stable DNS name. Use spring-cloud-kubernetes instead, which integrates with K8s Services and reads configuration from ConfigMaps and Secrets. Running Eureka on top of Kubernetes is operational duplication. Eureka is the right choice for non-K8s environments such as bare-metal or VM-based deployments.
How many microservices should a Spring Boot application start with?
Start with a modular monolith. Organise your codebase by domain package boundaries (orders, inventory, payments) but deploy as a single service initially. Extract a microservice when you have a concrete deployment-independence requirement — different release cadence, different scaling profile, different team ownership. For most applications, 3 to 7 services is a manageable scope. Teams that over-decompose into 20+ services on day one typically spend more time on infrastructure than on features.

Need Spring Boot Developers for Your Team?

Pillai Infotech supplies pre-vetted Java developers with hands-on Spring Boot and Spring Cloud production experience. From a single senior engineer to a full microservices team — India-based, cost-effective, and available within 2 weeks.

Hire Java Developers
Pillai Infotech Enterprise Team
Java, Spring Boot & Microservices Architecture

We build and staff enterprise microservices teams for banking, insurance, and B2B platforms using Spring Boot and Spring Cloud. Explore our custom software development services or hire Java developers to scale your existing team.