Note (2026): This was written while I was actively working with Vault. My current stack leans more toward AWS-native solutions (KMS, Secrets Manager, IAM), but the core concepts here—separating secrets from code and treating encryption as a service—still apply.
In my last post, I discussed using the Spring Encryption project to encrypt sensitive data in our application. As I started looking deeper into it, I realized that the particular implementation I was using (especially around queryable encryption) was no longer recommended and had been deprecated.
That led me to start looking for a better solution. Something that would work for both encryption and secrets management in a more production-ready way. That’s where Vault came in.
Vault provides a few key capabilities:
- Secrets management (no more plain text in config files)
- Encryption and decryption via the Transit engine
- Identity-based access to control who can access what
One thing I learned quickly is to avoid running Vault in dev mode for anything beyond quick experimentation. Dev mode skips important operational concepts like unsealing and persistence, which you’ll absolutely need to understand before moving to production. It’s much better to set up a local environment that behaves closer to how Vault runs in the real world.
On the Spring side, integrating Vault is fairly straightforward using the spring-cloud-starter-vault-config dependency. Once configured, Vault can supply values directly into your application properties.
For example:
@Value("${super.secret.value}")
private String secret;
With the proper Vault configuration and secret paths, this value is resolved at runtime without ever being stored in your codebase or config files.
It’s worth noting that this isn’t “magic.” Your application still needs to authenticate to Vault (in this example using a token, though in production you’d typically use something like AppRole, IAM, or another identity-based method), and your secret paths need to be structured correctly so Spring can resolve them.
For encryption, Vault’s Transit engine allows you to send data to Vault to be encrypted or decrypted without ever exposing the encryption keys to your application. That’s a big shift compared to managing keys yourself. Your app never sees them and Vault handles the cryptographic operations.
Vault also supports dynamic secrets, including database credentials. Instead of storing a static username and password, Vault can generate credentials on demand with a limited lifetime. This means you’re not responsible for manually rotating credentials or keeping long-lived secrets around.
I also looked at some of the additional Spring Cloud Vault dependencies, like database integration. These help wire up dynamic credentials into your application, though Vault itself is still responsible for generating and managing those credentials.
One small note on configuration: while @Value works well for simple cases, @ConfigurationProperties is often a better choice as your configuration grows, since it gives you type safety and a cleaner structure.
Overall, moving to Vault felt like a meaningful upgrade. Instead of thinking about encryption and secrets as something handled inside the application, they become externalized and managed through a dedicated system.
What I would do today
If I were building this today in AWS, and I often am, I would use:
- KMS for encryption
- Secrets Manager or Parameter Store for secrets
- IAM roles for service-to-service authentication
The tools have changed, but the patterns are the same: keep secrets out of code, avoid long-lived credentials, and push security concerns into systems designed to handle them.
