Learn how to use Doppler to supply secrets to Spring Boot applications in local development and Kubernetes.
- Java 18
- Maven
- Doppler CLI
Create the sample spring-boot-app Doppler project using the following button:
Or the Doppler CLI:
doppler import
Auto-select the project and dev config using the [doppler.yaml](./doppler.yaml] file:
doppler setup --no-interactive
Then verify you can fetch secrets:
doppler secrets
Then open the Project in the doppler dashboard:
doppler open dashboard
Doppler secret names are standardized to UPPER_SNAKE_CASE
which is compatible with Spring Boot's relaxed binding 2.0.
View the Project in the Doppler dashboard or open the doppler-template.yaml file for examples.
Configuration is centralized with an AppConfig.java class using Spring Boot configuration properties to bind secrets injected as environment variables by Doppler.
The configuration values are set by a combination of direct environment variable injection (Redis properties) and using an application.properties file with environment variable placeholders.
In local development, the Doppler CLI acts as an application runner, injecting secrets as environment variables into the Spring Boot process:
doppler run -- ./mvnw spring-boot:run --quiet
Because the Doppler CLI is required for injecting secrets into the Spring process, running and debugging your application is handled slightly differently.
Using IntelliJ IDEA to demonstrate, create a new Shell Script configuration and use the following as the Script text:
doppler run -- ./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005"
We recommend saving the run configuration as a project file so team members don't have to configure this individually.
Now that the server can be run in debug mode, the final step is creating a Remote JVM Debug configuration:
Running and debugging an application is then as follows.
ScreenFlow.mp4
First build the image:
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=doppler/spring-boot-app
Then use the Doppler CLI to set container environment variables via the --env-file
option:
docker run \
--rm \
--name doppler-spring-boot \
--env-file <(doppler secrets download --no-file --format docker) \
-p $(doppler secrets get SERVER_PORT --plain):$(doppler secrets get SERVER_PORT --plain) \
doppler-spring-boot
The quickest way to create and sync secrets to Kubernetes is using the Doppler CLI with kubectl
.
To create a or update a secret named spring-boot-app-secret
:
kubectl create secret generic spring-boot-app-secret \
--save-config \
--dry-run=client \
--from-env-file <(doppler secrets download --no-file --format docker) \
-o yaml | \
kubectl apply -f -
Then use the handy get-kube-secret.sh script to view the secrets contents:
./bin/get-kube-secret.sh spring-boot-app-secret
This adhoc approach is great for getting started but for production usage to sync secrets at scale, we recommend our Kubernetes Operator.
The Doppler Kubernetes Operator is a background service that automatically syncs secrets to Kubernetes with (optional) automatic redeployment of services when the secrets they consume are updated.
The Operator uses a DopplerSecret
CRD (custom resource definition) which provides the auth token required for fetching secrets as well as the name and namespace the Operator will create
Check out the Operator Secrets Sync Guide for more detailed instructions but to quickly demonstrate, first create a Service Token for the Production environment to grant read-only access:
doppler setup # Select the prd environment
DOPPLER_TOKEN="$(doppler configs tokens create "Doppler Kubernetes Operator" --plain)"
Then create a Kubernetes secret containing the Service Token:
kubectl create secret generic spring-boot-app-doppler-token \
--namespace doppler-operator-system \
--from-literal=serviceToken=$DOPPLER_TOKEN
Confirm the secret was created successfully:
./bin/get-kube-secret.sh spring-boot-app-doppler-token --namespace doppler-operator-system
Next, define the DopplerSecret
(see ./kubernetes/doppler-secret.yaml):
apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
# DopplerSecret Name
name: spring-boot-app-doppler-secret
# Namespace (only create DopplerSecret resources in the doppler-operator-system namespace)
namespace: doppler-operator-system
spec:
tokenSecret:
# Doppler token secret reference
name: spring-boot-app-doppler-token
managedSecret:
# Synced secret name
name: spring-boot-app-secret
# Synced secret namespace (the namespace of the deployment that will consume this secret)
namespace: default
Create the DopplerSecret
by running:
kubectl apply -f kubernetes/doppler-secret.yaml
Then verify Operator created the secret:
# View secret and Doppler-specific annotations
kubectl describe secret spring-boot-app-secret
# View secrets
./bin/get-kube-secret.sh spring-boot-app-secret
Now that the Kubernetes secret is in place, let's take care of deployment.
You'll notice that the below Deployment spec is pretty standard except for the secrets.doppler.com/reload: 'true'
annotation which will trigger a redeploy to update the application with the latest secrets.
apiVersion: apps/v1
kind: Deployment
metadata:
name: doppler-spring-boot
annotations:
# The Operator's real superpower
secrets.doppler.com/reload: 'true'
spec:
replicas: 1
selector:
matchLabels:
app: doppler-spring-boot
template:
metadata:
labels:
app: doppler-spring-boot
spec:
containers:
- name: doppler-spring-boot
image: doppler/spring-boot-app
imagePullPolicy: IfNotPresent
# envFrom injects all secret values as environment variables
envFrom:
- secretRef:
# Operator created secret
name: spring-boot-app-secret
ports:
- name: app
containerPort: 8080
resources:
requests:
memory: '1024Mi'
cpu: '250m'
limits:
memory: '1024Mi'
cpu: '500m'
Deploy the application:
kubectl apply -f kubernetes/deployment.yaml
Then tail the deployment logs to verify the secrets were injected successfully:
kubectl logs -f deployment/doppler-spring-boot --tail 50
To test the auto-deployment reload on secrets change, update a secret (e.g APP_APPLICATION_NAME) in the dashboard, then tail the logs again to observe deployment being updated.
If the log tail command is killed, it's because the Pod it was tailing was deleted. Simply re-run the above kubectl logs
command.
You can get help and support in our Doppler community forum, find us on Twitter, or Team and Enterprise customers can use our in-product support.