Home Java Spring Cloud Kubernetes: Service Discovery, ConfigMaps & Scaling
Advanced 4 min · July 14, 2026

Spring Cloud Kubernetes: Service Discovery, ConfigMaps & Scaling

Master Spring Cloud Kubernetes for service discovery, ConfigMaps, and scaling in production.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Java 17+
  • Spring Boot 3.x
  • Kubernetes cluster (minikube or cloud)
  • Basic understanding of Kubernetes concepts: pods, services, ConfigMaps, HPA
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Use Spring Cloud Kubernetes for native service discovery via Kubernetes API, not Eureka.
  • Externalize config with ConfigMaps and Secrets; avoid @Value for dynamic updates.
  • Autoscaling works best with custom metrics; HPA alone is not enough for burst traffic.
  • Beware of stale caches: Spring Cloud Kubernetes caches secrets/ConfigMaps by default.
✦ Definition~90s read
What is Guide to Spring Cloud Kubernetes?

Spring Cloud Kubernetes is a set of libraries that integrate Spring Boot applications with Kubernetes, providing service discovery, configuration management, and scaling capabilities using native K8s APIs.

Imagine your app is a food truck at a huge festival.
Plain-English First

Imagine your app is a food truck at a huge festival. Spring Cloud Kubernetes is like a festival organizer that tells each truck where the others are parked (service discovery), gives them the daily specials board (ConfigMaps), and calls in extra trucks when lines get long (scaling). Without it, each truck would have to radio every other truck manually and guess how many cooks to hire.

If you're running Spring Boot on Kubernetes and still using Eureka for service discovery, you're doing it wrong. I've seen teams deploy a five-node Eureka cluster on K8s only to realize Kubernetes already has a built-in service registry. Spring Cloud Kubernetes bridges the gap, letting you leverage native K8s APIs for service discovery, configuration, and scaling. But here's the kicker: the official docs make it look simple. They're not wrong, but they leave out the landmines. I've debugged production outages where a ConfigMap update took 15 minutes to propagate because of caching defaults. I've watched HPA thrash because we didn't understand pod metrics latency. This article gives you the real story—what works, what doesn't, and how to avoid the 2 AM pages.

Why Spring Cloud Kubernetes?

Let's cut through the hype. If you're already on Kubernetes, you have a service registry: the Kubernetes API server. You have DNS-based service discovery built into every pod. You have ConfigMaps and Secrets for configuration. So why add Spring Cloud Kubernetes? Because it gives you a unified, Spring-friendly way to interact with these primitives. Instead of manually parsing environment variables or calling the K8s API yourself, you get annotations like @DiscoveryClient and @ConfigurationProperties that work seamlessly with ConfigMaps. It also provides a reload mechanism so your app can pick up config changes without a full restart. But—and this is a big but—it's not magic. The abstraction leaks. I've seen teams blindly add the dependency and assume it works like Spring Cloud Config Server with Git. It doesn't. You trade one set of complexities for another.

pom.xmlJAVA
1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>
⚠ Dependency Choice Matters
📊 Production Insight
In a production incident, a team using Fabric8 saw OOM because the client's HTTP connection pool wasn't tuned for high-throughput ConfigMap watches. Switch to the official client or tune the pool.
🎯 Key Takeaway
Spring Cloud Kubernetes simplifies integration with K8s APIs but adds another layer of abstraction that can hide issues.

Service Discovery: Not Your Father's Eureka

Service discovery in K8s works via DNS. When you create a Service, K8s assigns a DNS name (e.g., my-service.namespace.svc.cluster.local). Pods can resolve this name to the service's cluster IP, which load-balances across ready pods. Spring Cloud Kubernetes wraps this with a DiscoveryClient implementation. You can inject @Autowired DiscoveryClient and call getInstances(). Under the hood, it queries the K8s API for endpoints. This is great for polyglot environments, but there's a catch: the default implementation caches the endpoint list for 30 seconds. If you have rapid scaling, you might route to a pod that's been terminated. I once debugged a payment service that occasionally returned 503s because the cache still pointed to a terminating pod. The fix: set spring.cloud.kubernetes.discovery.cache-watch-interval=5000 (5 seconds) and enable watch events. But be careful—too low and you hammer the API server.

DiscoveryController.javaJAVA
1
2
3
4
5
6
7
8
9
10
@RestController
public class DiscoveryController {
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/services/{serviceName}")
    public List<ServiceInstance> getInstances(@PathVariable String serviceName) {
        return discoveryClient.getInstances(serviceName);
    }
}
Output
[{"serviceId":"payment-service","host":"10.1.0.5","port":8080,"metadata":{}}]
💡Use DNS for Simple Lookups
📊 Production Insight
At a fintech startup, we had a 30-second cache that caused intermittent failures during rolling updates. We reduced it to 5 seconds and added a readiness probe that checks if the pod is in the service's endpoint list.
🎯 Key Takeaway
K8s DNS is fast and reliable for service discovery. Use Spring Cloud Kubernetes DiscoveryClient only when you need instance-level details.

ConfigMaps and Secrets: The Good, the Bad, the Stale

ConfigMaps are K8s objects that hold key-value pairs or entire config files. Spring Cloud Kubernetes can automatically populate your Environment from a ConfigMap. You can even map a ConfigMap to a @ConfigurationProperties class. Sounds perfect, right? Not quite. The default behavior is to watch for changes and update the Environment in real-time. But 'real-time' is relative. The watch uses a long-polling mechanism with a 30-second cache by default. And if your service account lacks 'watch' permission, it falls back to polling every 30 seconds. I've seen teams tear their hair out because config changes took minutes to propagate. The solution: ensure RBAC includes 'watch', reduce cacheMillis, and consider using the reload feature (spring.cloud.kubernetes.reload.enabled=true) with 'event' mode. But be warned: reloading the context can cause unexpected behavior if your beans are not designed for refresh.

application.propertiesJAVA
1
2
3
4
5
6
spring.cloud.kubernetes.config.enabled=true
spring.cloud.kubernetes.config.name=my-app-config
spring.cloud.kubernetes.config.namespace=default
spring.cloud.kubernetes.config.cacheMillis=5000
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.mode=event
⚠ Reload Mode Can Bite You
📊 Production Insight
A SaaS company used ConfigMap for feature flags. After a reload, a singleton scheduler bean was destroyed and recreated, causing duplicate scheduled tasks. They had to implement a custom lifecycle handler.
🎯 Key Takeaway
ConfigMap updates are not instant. Use watches, reduce cache, and test the reload behavior with your specific beans.

Scaling with HPA and Custom Metrics

Horizontal Pod Autoscaler (HPA) is the standard way to scale your Spring Boot app on K8s. It adjusts the number of replicas based on CPU, memory, or custom metrics. But here's the hard truth: default CPU-based scaling is often too slow for burst traffic. I've seen a payment gateway get overwhelmed because HPA took 2 minutes to react to a spike. The issue is that metrics-server polls kubelet every 15 seconds, and HPA evaluates every 30 seconds by default. The total lag can be over a minute. For faster scaling, use custom metrics (e.g., request rate via Prometheus Adapter) and tune the HPA behavior with the 'behavior' field (available in K8s 1.18+). Also, set resource requests and limits correctly—without them, HPA won't work. And don't forget to set the '--horizontal-pod-autoscaler-sync-period' flag on the controller manager if you need sub-30-second evaluation.

hpa.yamlJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: payment-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: payment-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: 100
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
🔥Custom Metrics Require Adapter
📊 Production Insight
An e-commerce site saw HPA scale down too aggressively after a flash sale, leaving them under-provisioned for the next wave. They added a stabilization window of 5 minutes for scale-down.
🎯 Key Takeaway
Default HPA is too slow for burst traffic. Use custom metrics and tune the behavior to scale faster.

What the Official Docs Won't Tell You

Let's get real about the gaps. First, the official docs emphasize that Spring Cloud Kubernetes makes your app 'Kubernetes-native'. They don't mention that it also makes your app dependent on the K8s API server being available. If your API server goes down, your service discovery and config updates break. I've seen this happen during a cluster upgrade. Second, the caching defaults are dangerous. The docs say 'config.cacheMillis=30000' is fine, but in a production incident, that 30-second cache caused a 5-minute propagation delay because the watch event was lost and the fallback polling interval was also 30 seconds. Third, the reload feature is marketed as 'zero-downtime config updates'. It's not. If you have beans that hold state (e.g., connection pools), refreshing the context can close those pools and recreate them, causing transient failures. Fourth, the Fabric8 client has a memory leak in some versions (I'm looking at you, 4.11.x) when watches disconnect and reconnect. The official client is better but still not perfect. Finally, the docs don't warn you about RBAC. The default service account in many clusters has no 'watch' permission, so ConfigMap updates silently fall back to polling. You'll think your app is watching, but it's not.

⚠ Always Test with a Canary
📊 Production Insight
During a K8s version upgrade, the API server was briefly unavailable. Our app couldn't discover services and started throwing exceptions. We added a fallback to cached service endpoints and a circuit breaker for discovery calls.
🎯 Key Takeaway
The official docs gloss over caching, RBAC, and the fragility of watch-based updates. Always test in a staging environment that mirrors production.

Best Practices from the Trenches

After countless production incidents, here's my playbook for Spring Cloud Kubernetes. First, use the official Kubernetes Java client (spring-cloud-kubernetes-client) instead of Fabric8. It's lighter, better maintained, and has fewer memory issues. Second, set cacheMillis to 5000 for both config and discovery. If you're worried about API server load, use a watch with a backoff—but don't rely on polling. Third, for critical configs, avoid the reload feature entirely. Instead, use a sidecar like Reloader that triggers a rolling restart when a ConfigMap changes. It's simpler and more predictable. Fourth, always set resource requests and limits on your pods. Without them, HPA won't work and your pods may be throttled. Fifth, monitor the Kubernetes API server metrics (especially request latency and error rate) because your app's health depends on it. Sixth, use readiness probes that check if the pod is actually serving traffic, not just if the JVM is alive. Finally, test your scaling behavior with load tests that simulate burst traffic. Tune the HPA behavior until you're comfortable with the response time.

deployment.yamlJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
      - name: payment-service
        image: payment-service:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
💡Use Actuator for Probes
📊 Production Insight
A team used the reload feature and saw intermittent failures because a @RefreshScope bean was not properly reinitialized. They switched to Reloader sidecar and never looked back.
🎯 Key Takeaway
Simplify by avoiding the reload feature for critical configs. Use sidecars and rolling restarts instead.
● Production incidentPOST-MORTEMseverity: high

The ConfigMap Cache That Killed Our Canary

Symptom
After updating a feature flag ConfigMap, the canary pods continued using the old flag for nearly 15 minutes. Users saw inconsistent behavior.
Assumption
The developer assumed that ConfigMap changes would be reflected immediately via Spring Cloud Kubernetes' property reload.
Root cause
Spring Cloud Kubernetes caches ConfigMap data for 30 seconds by default, but the Kubernetes watch event was delayed due to a misconfigured RBAC permission on the service account. The pod couldn't receive watch events, so it fell back to polling every 30s. Combined with a long refresh interval in the app, the total delay hit 15 minutes.
Fix
We granted the service account 'watch' permission on ConfigMaps, reduced spring.cloud.kubernetes.config.cacheMillis to 5000, and added a health check that compares ConfigMap resourceVersion with the cached version.
Key lesson
  • Always verify RBAC permissions for watch operations—missing watch forces polling.
  • Never assume ConfigMap updates are instant; test with a canary and monitor propagation delay.
  • Use spring.cloud.kubernetes.config.cacheMillis=5000 for faster updates in dev, but beware of API server load.
  • Add a health indicator that exposes the cached resourceVersion for debugging.
  • For critical configs, consider using a sidecar like Reloader to trigger pod restarts.
Production debug guideSymptom to Action4 entries
Symptom · 01
Service A cannot call Service B by name
Fix
Check that Service B's Kubernetes service exists and that A's pod has DNS resolution. Run 'kubectl exec -it <pod> -- nslookup <service-name>' inside the pod.
Symptom · 02
ConfigMap changes not picked up
Fix
Check pod logs for 'ConfigMap watcher' messages. Verify service account has 'watch' permission on ConfigMaps. Inspect spring.cloud.kubernetes.config.cacheMillis setting.
Symptom · 03
HPA not scaling despite high CPU
Fix
Check if metrics-server is installed and reporting metrics. Verify HPA target metric name. Use 'kubectl describe hpa' to see scaling decisions.
Symptom · 04
Pod restarts after ConfigMap update
Fix
Check if you're using spring.cloud.kubernetes.reload.enabled=true with 'event' mode. The reload may trigger a context refresh that restarts the pod if beans are not reloadable.
★ Quick Debug Cheat SheetCommon symptoms and immediate actions for Spring Cloud Kubernetes issues.
Service discovery not working
Immediate action
Check DNS resolution inside pod
Commands
kubectl exec -it <pod> -- nslookup <service-name>
kubectl get endpoints <service-name>
Fix now
Ensure service name matches and pods are ready
ConfigMap not updating+
Immediate action
Check pod logs for watcher errors
Commands
kubectl logs <pod> | grep -i configmap
kubectl describe configmap <name>
Fix now
Grant watch permission or reduce cacheMillis
HPA not scaling+
Immediate action
Verify metrics-server and HPA status
Commands
kubectl get hpa
kubectl describe hpa <name>
Fix now
Check target metric name and resource requests
FeatureSpring Cloud KubernetesSpring Cloud EurekaSpring Cloud Config Server
Service DiscoveryUses K8s API/DNSEureka serverNot applicable
ConfigurationConfigMaps/SecretsNot applicableGit-backed config server
ScalingHPA with custom metricsNot built-inNot built-in
CachingConfigurable cache, watchesEureka client cacheConfig client cache
K8s DependencyRequires K8s APINo K8s requiredNo K8s required
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
pom.xmlWhy Spring Cloud Kubernetes?
DiscoveryController.java@RestControllerService Discovery
application.propertiesspring.cloud.kubernetes.config.enabled=trueConfigMaps and Secrets
hpa.yamlapiVersion: autoscaling/v2Scaling with HPA and Custom Metrics
deployment.yamlapiVersion: apps/v1Best Practices from the Trenches

Key takeaways

1
Spring Cloud Kubernetes leverages native K8s APIs, but you must understand caching and RBAC to avoid production issues.
2
For service discovery, prefer K8s DNS; use DiscoveryClient only when you need instance metadata.
3
ConfigMap updates are not instant—reduce cacheMillis and ensure watch permissions, or use a sidecar for rolling restarts.
4
HPA with default settings is too slow for burst traffic; use custom metrics and tune the behavior.
5
Test everything in a staging environment that mirrors production, especially ConfigMap propagation and scaling.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain how Spring Cloud Kubernetes implements service discovery. What a...
Q02SENIOR
How does Spring Cloud Kubernetes handle ConfigMap updates? What are the ...
Q03SENIOR
Describe how to set up HPA for a Spring Boot app on Kubernetes. What met...
Q01 of 03SENIOR

Explain how Spring Cloud Kubernetes implements service discovery. What are the caching implications?

ANSWER
It implements DiscoveryClient by querying the Kubernetes API for endpoints of a service. The results are cached for a configurable interval (default 30s). This can cause stale routes if pods are terminated quickly. To mitigate, reduce cacheMillis or use watches.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
Is Spring Cloud Kubernetes production-ready?
02
Should I use Eureka or Kubernetes service discovery?
03
How do I speed up ConfigMap propagation?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Cloud. Mark it forged?

4 min read · try the examples if you haven't

Previous
Writing Custom Spring Cloud Gateway Filters: Global, Rate Limiting, and Security
17 / 34 · Spring Cloud
Next
Serverless Functions with Spring Cloud Function: AWS Lambda and Azure Functions