Spring Cloud Config Server vs Kubernetes ConfigMaps: When to Use Each
I was staring at my Spring Boot microservices deployment configuration, trying to decide whether to keep our Spring Cloud Config Server or migrate everything to Kubernetes ConfigMaps with GitOps. The team was split. Some argued Config Server is battle-tested, others said GitOps is the modern standard. I needed to figure out which approach made sense for our Kubernetes-native environment.
The Problem: Configuration Management Crossroads
We had been running Spring Cloud Config Server for years. It worked fine. But when we moved to Kubernetes and started using ArgoCD, I noticed something odd - we were managing configuration in two places. Config Server for Spring properties, and ConfigMaps for Kubernetes-native applications. This felt redundant.
The real question hit me during a Reddit discussion: “Do people still use config server in the age of gitops?”
The answer isn’t a simple yes or no. It depends on your environment, your team’s skills, and your operational requirements. Let me walk through what I learned from both approaches.
How Spring Cloud Config Server Works
I first tried Spring Cloud Config Server because our team was deeply invested in the Spring ecosystem. Here’s the architecture:
Config Server (Spring Boot) | v Git/SVN Backend | v Microservices poll or subscribe for changesThe setup is straightforward. You create a Spring Boot application that serves as the config server, pointing to a Git repository:
server: port: 8888
spring: cloud: config: server: git: uri: https://github.com/myorg/config-repo search-paths: - '{application}' default-label: mainThen your microservices connect to it:
spring: application: name: my-service cloud: config: uri: http://config-server:8888 fail-fast: true retry: initial-interval: 1000 max-attempts: 6What I liked about Config Server:
-
Dynamic refresh without restart - This was the killer feature. I could change a property, call the
/actuator/refreshendpoint, and the application would pick up the new value without restarting. -
Works outside Kubernetes - When we needed to run some services on VMs, Config Server just worked. No Kubernetes dependency.
-
Multiple backend support - Git, SVN, Vault, CredHub. We could even use the file system for local development.
What frustrated me:
-
Extra service to maintain - Config Server became another thing to monitor, scale, and troubleshoot. When it went down, services couldn’t start.
-
Not GitOps-native - While the config was in Git, the deployment model was push-based. Services had to actively fetch from Config Server. This didn’t fit our GitOps workflow where ArgoCD syncs everything from Git.
-
Spring-centric - As we added Go and Python microservices, they couldn’t use Config Server without significant effort.
How Kubernetes ConfigMaps with GitOps Works
Then I tried the Kubernetes-native approach with ArgoCD and Helm. The architecture looks like this:
Git Repository | v ArgoCD --> Sync to Cluster | v Helm Charts --> Generate ConfigMaps/Secrets | v Pods mount ConfigMaps as volumes or env varsHere’s how I structured it. First, the Helm values file containing the configuration:
config: database: host: postgres.prod.svc.cluster.local port: 5432 name: myapp redis: host: redis.prod.svc.cluster.local port: 6379 featureFlags: enableNewUI: true maxConnections: 100Then the ConfigMap template in the Helm chart:
apiVersion: v1kind: ConfigMapmetadata: name: {{ .Release.Name }}-config labels: app: {{ .Release.Name }}data: application.yml: | spring: datasource: url: jdbc:postgresql://{{ .Values.config.database.host }}:{{ .Values.config.database.port }}/{{ .Values.config.database.name }} redis: host: {{ .Values.config.redis.host }} port: {{ .Values.config.redis.port }} feature: new-ui: {{ .Values.config.featureFlags.enableNewUI }} max-connections: {{ .Values.config.featureFlags.maxConnections }}The deployment mounts this ConfigMap:
apiVersion: apps/v1kind: Deploymentmetadata: name: {{ .Release.Name }}spec: template: spec: containers: - name: app image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" volumeMounts: - name: config mountPath: /config readOnly: true volumes: - name: config configMap: name: {{ .Release.Name }}-configWhat I loved about this approach:
-
Git as single source of truth - Every config change goes through Git. Full audit trail. Easy rollback with
git revert. -
ArgoCD handles everything - I push to Git, ArgoCD syncs to the cluster. No manual deployment steps.
-
Language agnostic - Works with Java, Go, Python, Node.js. Anything that can read from environment variables or files.
-
Native Kubernetes - ConfigMaps are built into Kubernetes. No extra services to maintain.
The problem I hit:
ConfigMap changes don’t automatically propagate to running pods. I updated a config, ArgoCD synced the ConfigMap, but my pods kept using the old configuration. This was a critical issue.
Solving the ConfigMap Hot-Reload Problem
I found three solutions to this problem. Let me show you each one.
Solution 1: Immutable ConfigMap with Hash Annotation
The cleanest approach is to include a hash of the ConfigMap content in the deployment annotation. When the ConfigMap changes, the hash changes, triggering a new rollout.
apiVersion: apps/v1kind: Deploymentmetadata: name: {{ .Release.Name }}spec: template: metadata: annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} spec: containers: - name: app # ... container specNow every time I change values.yaml, Helm generates a new hash, and Kubernetes rolls out new pods with the updated config. Simple and effective.
Solution 2: Stakater Reloader
For an automated approach, I installed Stakater Reloader. It watches ConfigMaps and Secrets, and triggers rollouts when they change.
apiVersion: apps/v1kind: Deploymentmetadata: annotations: reloader.stakater.com/auto: "true" name: my-appspec: # ... deployment specI installed it with Helm:
helm repo add stakater https://stakater.github.io/stakater-chartshelm repo updatehelm install reloader stakater/reloader --namespace reloader --create-namespaceNow when I update a ConfigMap, Reloader detects the change and triggers a rolling restart of any deployment with the annotation.
Solution 3: Spring Cloud Kubernetes Config
If you’re staying in the Spring ecosystem but want Kubernetes-native config, Spring Cloud Kubernetes Config bridges the gap. It reads ConfigMaps directly and supports hot reload:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId></dependency>spring: application: name: my-service cloud: kubernetes: config: name: my-service-config namespace: default reload: enabled: true mode: eventThis gives you hot-reload with ConfigMaps, combining the best of both worlds.
Decision Matrix: When to Use What
After testing both approaches extensively, I created this decision matrix:
| Criteria | Spring Cloud Config Server | ConfigMaps + GitOps |
|---|---|---|
| Kubernetes-native | No | Yes |
| Non-K8s support | Yes (VMs, bare metal) | No |
| GitOps workflow | Limited (push-based) | Excellent (pull-based) |
| Hot reload without restart | Yes (native) | Requires tooling |
| Version control | Git backend | Git native |
| Language agnostic | No (Spring-focused) | Yes |
| Operational complexity | Higher (extra service) | Lower (native K8s) |
| Audit trail | Custom logging | Built-in (Git history) |
| Mixed environments | Excellent | Limited |
My Recommendation: Use ConfigMaps for Kubernetes-Native Environments
For teams running on Kubernetes with GitOps practices, ConfigMaps with ArgoCD/Helm is the better choice. Here’s why:
-
Single source of truth - Git holds everything: application code, infrastructure, and configuration.
-
Simpler operations - No Config Server to maintain, monitor, or troubleshoot.
-
Better audit trail - Every config change is a Git commit with author, time, and reason.
-
Works across languages - Your Go and Python services can use the same config system.
But keep Spring Cloud Config Server if:
- You’re running on VMs or bare metal
- You have legacy Spring Boot applications outside Kubernetes
- You need hot-reload without pod restarts AND can’t add Reloader
- Your team lacks Kubernetes expertise
Common Mistakes I Made
Mistake 1: Not handling ConfigMap propagation
I assumed ConfigMap changes would automatically reach running pods. They don’t. I had production incidents where config changes weren’t applied because pods were using cached configs.
Fix: Use hash-based annotations or Stakater Reloader.
Mistake 2: Over-engineering with both systems
I tried running Config Server alongside ConfigMaps. This created confusion about where to put configs and added operational overhead.
Fix: Pick one approach per environment. Don’t mix unless absolutely necessary.
Mistake 3: Ignoring GitOps benefits
I initially dismissed GitOps as “just another trend.” Then I accidentally deleted a config in Config Server with no audit trail. Recovery was painful.
Fix: Embrace GitOps. The audit trail and rollback capabilities alone are worth it.
Mistake 4: Not versioning ConfigMaps properly
I used plain ConfigMaps without any versioning strategy. When something broke, I couldn’t tell which config version caused it.
Fix: Include version in ConfigMap names or use Helm’s hash annotation for automatic versioning.
Migration Path: Config Server to GitOps
If you’re currently on Config Server and want to migrate to GitOps, here’s the path I recommend:
Phase 1: Parallel running
Deploy ArgoCD and start managing new services with ConfigMaps. Keep Config Server for existing services.
Phase 2: Convert configurations
Translate application-{profile}.yml files to Helm values:
# From Config Server repoapplication-prod.yml -> values-prod.yaml
# Example conversionspring.datasource.url=jdbc:postgresql://prod-db:5432/myapp# becomesconfig: database: url: jdbc:postgresql://prod-db:5432/myappPhase 3: Deploy with ArgoCD
Create ArgoCD applications for each service, pointing to your Helm charts.
Phase 4: Add hot-reload support
Install Stakater Reloader and add annotations to deployments.
Phase 5: Deprecate Config Server
Once all services are migrated, shut down Config Server.
Related Knowledge
GitOps Principles
GitOps is built on four principles:
- Declarative - Everything described declaratively in Git
- Versioned - Git provides version control and history
- Automated - Changes are automatically applied
- Reconciled - System continuously matches desired state
ConfigMap Best Practices
- Keep ConfigMaps immutable (use
immutable: truefield) - Use separate ConfigMaps for different concerns (database, feature flags, etc.)
- Never store sensitive data in ConfigMaps (use Secrets instead)
- Include environment in ConfigMap names for clarity
Spring Cloud Config Alternatives
If you need config management outside Kubernetes but don’t want Spring Cloud Config:
- Consul KV - HashiCorp’s service discovery with config storage
- etcd - Distributed key-value store
- HashiCorp Vault - Secrets management with dynamic config
- AWS Parameter Store - Cloud-native config storage
Final Thoughts
The Reddit discussion that sparked my investigation had a clear consensus: GitOps has largely replaced Config Server in Kubernetes-native environments. The top comment summed it up:
“We use ArgoCD, helm for templating and mount configmaps and secrets. Where would the config server be a better option?”
The answer is simple: only when you’re not on Kubernetes, or when you have specific needs that ConfigMaps can’t meet.
For Kubernetes deployments in 2024 and beyond, use ConfigMaps with GitOps. Add Stakater Reloader or hash-based annotations for automatic rollouts. Your future self will thank you for the cleaner architecture, better audit trails, and simpler operations.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Reddit Discussion: Config Server in the age of GitOps
- 👨💻 Stakater Reloader Documentation
- 👨💻 ArgoCD Documentation
- 👨💻 Spring Cloud Config Documentation
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments