It's not always a great idea to tie your configuration to your application.

Yet that's what lots of Spring Boot developers do when they put the application.properties or application.yml file in src/main/resources.

It's not catastrophic to do things like that. But do you really want to rebuild and redploy every time you have a configuration change? 

Probably not.

Fortunately, there's a way out: a JSON configuration object that's stored as part of your Kubernetes environment.

So if you're not deploying to a Kubernetes cluster, you might as well stop reading now. Because this guide is for Kubenetes users.

External Life

The configuration option that I recommend is to set your properties in a JSON object with the SPRING_APPLICATION_JSON environment variable referencing it.

So let me back up and explain a couple of things here.

First: yes, you can put your Spring Boot properties in JSON format. They don't have to follow the standard .properties or YAML rules.

Quite frankly, I'm not sure why more people don't use JSON properties. 

Secondly: you've got to tell Spring where to find that JSON configuration. And you do that with the SPRING_APPLICATION_JSON environment variable.

But you're operating in a Kubernetes cluster, where things are a little different than what you'd expect with a manual deployment of a JAR file or even a standalone Docker deployment.

So you've got to create a ConfigMap object. Then, you've got to update your Deployment YAML file for the application you're deploying to refrence that object.

If that sounds complicated, it's not. I'll walk you through how to do it here.

Create the ConfigMap File

First up: create the ConfigMap file. It's a standard Kubernetes object that follows the pattern you've probably seen countless times.

Here's what my file looks like:

kind: ConfigMap
apiVersion: v1
metadata:
  name: config-ecosystem
data:
  config.json: >-
    { 
    "user.files.base.path" : "/etc/careydevelopment/users", "contact.files.base.path" : "/etc/careydevelopment/contacts",
    "google.api.oauth2.token.url" : "https://www.googleapis.com/oauth2/v4/token",
    "google.api.oauth2.auth.url" : "https://accounts.google.com/o/oauth2/v2/auth",
    }

Pay attention to the data section. In there, I give a name to my configuration. I imaginatively call it config.json.

You can call it whatever you want. But just remember the name. You'll need to reference it later.

Then take a look at the name/value assignments inside the curly braces. They follow the standard JSON convention with string-to-string assignments.

However, the names do look like something you'd see in an application.properties file (for example, "user.files.base.path"). That's okay, though.

If I ever revert back to a standard .properties format, I won't have to change the code that references these property names.

Also, pay attention to metadata. I call this object config-ecosystem.

You'll note that nothing in the configuration is confidential. That's because you shouldn't store confidential stuff (like passwords) in a ConfigMap object.

Use a Secret if you need to keep prying eyes away from config items.

Next, I'll go ahead and save that file in my Kubernetes environment as config-ecosystem.yaml.

Then, I'll run this at the command line:

kubectl apply -f config-ecosystem.yaml

And that will create the ConfigMap object.

If you're using Microsoft Azure (as I am), you can also just visit the Kubernetes UI and click Add at the top of the Configuration screen.

 

Then paste in the YAML code and click the Add button at the bottom.

 

However you want to do it (via the command line or UI) is up to you. It will work either way.

Referencing the Configuration

Up to this point, all you've done is deployed a configuration. You haven't referenced it yet.

To do that, you'll need to update your application's Deployment YAML. Here's what mine looks like:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ecosystem-user-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ecosystem-user-service
  template:
    metadata:
      labels:
        app: ecosystem-user-service
    spec:
      nodeSelector:
        "kubernetes.io/os": linux
      volumes:
      - name: volume
        persistentVolumeClaim:
          claimName: carey-file
      containers:
      - name: ecosystem-user-service
        image: registry.hub.docker.com/careydevelopment/ecosystem-user-service:latest
        env:
        - name: SPRING_APPLICATION_JSON
          valueFrom:
            configMapKeyRef:
              name: config-ecosystem
              key: config.json
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 400m
            memory: 512Mi
        volumeMounts:
        - mountPath: "/etc/careydevelopment"
          name: volume          
        ports:
          - containerPort: 8080

Pay particular attention to the env object under containers. That's what I'll zoom in on here.

First of all, env unsurprisingly sets an environment variable. And that's what you need to do with Spring if you want to use an external JSON configuration.

The name of the environment variable you need to set is SPRING_APPLICATION_JSON. That will tell Spring where to look for the JSON object.

But now you have to point that environment variable to a JSON object. That's what valueFrom does.

And since you're getting the JSON from a ConfigMap, you'll need to specify those details using the configMapKeyRef object.

The name property in that object specifies the name of the ConfigMap that holds the JSON object. In this case, it's config-ecosystem and indeed that matches the metadata name of the ConfigMap object above.

But which property in the ConfigMap object holds the JSON object? That question is answered with the key property.

In the YAML above, key is set to config.json and that is, in fact, the unimaginitive name I gave my JSON object back in the ConfigMap file.

Once the YAML is updated, you need to redeploy. I usually take the easy route and delete the deployment workload from the UI and then use that Add button to paste in the new YAML.

You can also use kubectl delete to delete your workload and then use kubectl apply to load the new YAML from a file if you like to go the command-line route.

Once it's redeployed, wait a minute or so and test it out.

Wrapping It Up

That wasn't so bad, was it?

Now you know how to use an external configuration in a Kubernetes cluster. And it's a JSON configuration!

Remember: don't use this solution for confidential info. Use a Secret instead.

Just make sure you have fun!