You’re familiar with twelve-factor apps? This relates to a set of principles shared by Heroku over a decade ago. The thinking goes, if your app adheres to these principles, it’s more likely to be scalable, resilient, and portable. While twelve-factor apps were introduced before Docker, serverless, or mainstream cloud adoption were a thing, I think these principles remain relevant in 2022. One of those principles relates to externalizing your configuration so that environment-related settings aren’t in code. Spring Cloud Config is a fun project that externalizes configurations for your (Java) app. It operates as a web server that serves up configurations sourced from a variety of places including git repos, databases, Vault, and more. A month ago, I saw a single-line mention in the Spring Cloud release notes that said Spring Cloud Config now integrates with Google Cloud Secret Manager. No documentation or explanation of how to use this feature? CHALLENGE ACCEPTED.
To be sure, a Spring Boot developer can easily talk to Google Cloud Secret Manager directly. We already have a nice integration here. Why add the Config Server as an intermediary? One key reason is to keep apps from caring where the configs come from. A (Spring Boot) app just needs to make an HTTP request or use the Config Client to pulls configs, even if they came from GitHub, a PostgreSQL database, Redis instance, or Google Cloud Secret Manager. Or any combination of those. Let’s see what you think once we’re through.
Setting up our config sources
Let’s pull configs from two different places. Maybe the general purpose configuration settings are stored in git, and the most sensitive values are stored in Secret Manager.
My GitHub repo has a flat set of configuration files. The Spring Cloud Config Server reads all sorts of text formats. In this case, I used YAML. My “app1” has different configs for the “dev” and “qa” environments, as determined by their file names.

Secret Manager configs work a bit differently than git-based ones. The Spring Cloud Config Server uses the file name in a git repo to determine the app name and profile (e.g. “app1-qa.yml”) and makes each key/value pair in that file available to Spring for binding to variables. So from the image above, those three properties are available to any instance of “app1” where the Spring profile is set to “qa.” Secret Manager itself is really a key/value store. So the secret name+value is what is available to Spring. The “app” and “profile” come from the labels attached to the secret. Since you can’t have two secrets with the same name, if you want one secret for “dev” and one for “qa”, you need to name them differently. So, using the Cloud Code extension for VS Code, I created three secrets.

Two of the secrets (connstring-dev, connstring-qa) hold connection strings for their respective environments, and the other secret (serviceaccountcert) only applies to QA, and has the corresponding label values.
Ok, so we have all our source configs. Now to create the server that swallows these up and flattens the results for clients.
Creating and testing our Spring Cloud Config Server
Creating a Spring Cloud Config Server is very easy. I started at the Spring Intializr site to bootstrap my application. In fact, you can click this link and get the same package I did. My dependencies are on the Actuator and Config Server.

The Google Cloud Secret Manager integration was added to the core Config Server project, so there’s config-specific dependency to add. It does appear you need to add a reference to the Secret Manager package to enable connectivity and such. I added this to my POM file.
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
<version>1.0.1</version>
</dependency>
There’s no new code required to get a Spring Cloud Config Server up and running. Seriously. You just add an annotation (@EnableConfigServer) to the primary class.
@EnableConfigServer
@SpringBootApplication
public class BootConfigServerGcpApplication {
public static void main(String[] args) {
SpringApplication.run(BootConfigServerGcpApplication.class, args);
}
}
The final step is to add some settings. I created an application.yaml file that looks like this:
server:
port: ${PORT:8080}
spring:
application:
name: config-server
profiles:
active:
secret-manager, git
cloud:
config:
server:
gcp-secret-manager:
#application-label: application
#profile-label: profile
token-mandatory: false
order: 1
git:
uri: https://github.com/rseroter/spring-cloud-config-gcp
order: 2
Let’s unpack this. First I set the port to whatever the environment provides, or 8080. I’m setting two active profiles here, so that I activate the Secret Manager and git environments. For the “gcp-secret-manager” block, you see I have the option to set the label values to designate the application and profile. If I wanted to have my secret with a label “appname:app1” then I’d set the application-label property here to “appname.” Make sense? I fumbled around with this for a while until I understood it. And notice that I’m pointing at the GitHub repo as well.
One big thing to be aware of on this Secret Manager integration with Config Server. Google Cloud has the concept of “projects.” It’s a key part of an account hierarchy. You need to provide the project ID when interacting with the Google Cloud API. Instead of accepting this as a setting, the creators of the Secret Manager integration look up the value using a metadata service that only works when the app is running in Google Cloud. It’s a curious design choice, and maybe I’ll submit an issue or pull request to make that optional. In the meantime, it means you can’t test locally; you need to deploy the app to Google Cloud.
Fortunately, Google Cloud Run, Secret Manager, and Artifact Registry (for container storage) are all part of our free tier. If you’re logged into the gcloud CLI, all you have to do is type gcloud run deploy
and we take your source code, containerize it using buildpacks, add it to Artifact Registry, and deploy a Cloud Run instance. Pretty awesome.

After a few moments, I have a serverless container running Spring middleware. I can scale to zero, scale to 1, handle concurrent requests, and maybe pay zero dollars for it all.

Let’s test this out. We can query a Config Server via HTTP and see what a Spring Boot client app would get back. The URL contains the address of the server and path entries for the app name and profile. Here’s the query for app1 and the dev profile.

See that our config server found two property sources that matched a dev profile and app1. This gives a total of three properties for our app to use.
Let’s swap “dev” for “qa” in the path and get the configurations for the QA environment.

The config server used different sources, and returns a total of five properties that our app can use. Nice!
Creating and testing our config client
Consuming these configurations from a Spring Boot app is simple as well. I returned to the Spring Initializr site and created a new web application that depends on the Actuator, Web, and Config Client packages. You can download this starter project here.

My demo-quality code is basic. I annotated the main class as a @RestController
, exposed a single endpoint at the root, and returned a couple of configuration values. Since the “dev” and “qa” connection strings have different configuration names—remember, I can’t have two Secrets with the same name—I do some clunky work to choose the right one.
@RestController
@SpringBootApplication
public class BootConfigClientGcpApplication {
public static void main(String[] args) {
SpringApplication.run(BootConfigClientGcpApplication.class, args);
}
@Value("${appversion}")
String appVersion;
@Value("${connstring-dev:#{null}}")
String devConnString;
@Value("${connstring-qa:#{null}}")
String qaConnString;
@GetMapping("/")
public String getData() {
String secret;
secret = (devConnString != null) ? devConnString : qaConnString;
return String.format("version is %s and secret is %s",appVersion, secret);
}
}
The application.yaml file for this application has a few key properties. First, I set the spring.application.name
, which tells the Config Client which configuration properties to retrieve. It’ll query for those assigned to “app1”. Also note that I set the profile to “dev”, which also impacts the query. And, I’m exposing the “env” endpoint of the actuator, which lets me peek at all the environment variables available to my application.
server:
port: 8080
management:
endpoints:
web:
exposure:
include: env
spring:
application:
name: app1
profiles:
active: dev
config:
import: configserver:https://boot-config-server-gcp-ofanvtevaa-uw.a.run.app
Ok, let’s run this. I can do it locally, since there’s nothing that requires this app to be running in any particular location.

Cool, so it returned the values associated with the “dev” profile. If I stop the app, switch the spring.profiles.active
to “qa” and restart, I get different property values.

So the Config Client in my application is retrieving configuration properties from the Config Server, and my app gets whatever values make sense for a given environment with zero code changes. Nice!
If we want, we can also check out ALL the environment variables visible to the client app. Just send a request to the /actuator/env
endpoint and observe.

Summary
I like Spring Cloud Config. It’s a useful project that helps devs incorporate the good practice of externalizing configuration. If you want a bigger deep-dive into the project, check out my new Pluralsight course that covers it.
Also, take a look at Google Cloud Run as a legit host for your Spring middleware and apps. Instead of over-provisioning VMs, container clusters, or specialized Spring runtimes, use a cloud service that scales automatically, offers concurrency, supports private traffic, and is pay-for-what-you-use.
Thank you for this. I’ve gone through this with a debugger multiple times and the conditional between the metadata service or optionally giving a project is just odd. With the project submitted as a header it requires a prefix which changes how the entire client behaves. I’d prefer to work locally with this stuff, but I’ve at least got a staging env in gcp.
Anyway this was a great sanity check. Now i wish they would explain some of the rationale behind the file routing or how to keep configs for different apps in different folders without having a ton of things in the search path.
Oh well, such is the way of spring.
Yes, definitely some quirks to it!
Hi Richard, thank you for this wonderful article. Unfortunately for me GCP Secret Manager did not work.
I tried to find “gcp-secret-manager” support as one of the backends (something like git, credhub, vault etc.) in the documentation (https://cloud.spring.io/spring-cloud-config/reference/html/#_environment_repository).
Can you please help me with the documentation link that provides some indication that “gcp-secret-manager” is a supported backend.
Appreciate your help.
I got this working with proper permission to my service account.
Excellent!