skip to content

What is Spring Cloud Vault, and how do secrets stored in HashiCorp Vault end up as ordinary properties your beans can inject?

level: juniorimportance: must knowfreq 60%

answer

  1. Vault -> VaultPropertySource -> Environment
  2. spring.config.import=vault://
  3. VaultPropertySourceLocator (bootstrap) vs VaultConfigDataLoader (config-data)
  4. path = backend/application-name(/profile)
  5. auth: TOKEN / APPROLE / KUBERNETES

basics

~10 s

Spring Cloud Vault reads secrets from HashiCorp Vault at startup and adds them to Spring's Environment as a property source, so @Value and @ConfigurationProperties inject them just like application.yml values.

solid answer

~40 s

HashiCorp Vault is a server that stores secrets (passwords, API keys, certs). Spring Cloud Vault connects to it early in startup and registers a `VaultPropertySource` (via `VaultPropertySourceLocator`) into Spring's `Environment`. Because it becomes a `PropertySource`, secrets are resolved the same way as any other property — `@Value("${db.password}")` or `@ConfigurationProperties` — with no Vault-specific code in your beans. Modern setup (Spring Boot 2.4+) triggers it with `spring.config.import=vault://` and the Config Data API; older setups used the bootstrap context. You configure the Vault URI and an authentication method (token, AppRole, Kubernetes). By default it reads from a KV path derived from `spring.application.name` and active profiles, so `secret/myapp` maps to properties for the `myapp` service.

code

java · 16 lines
java
// application.yml
// spring:
//   application: { name: myapp }
//   config: { import: "vault://secret/myapp" }
//   cloud:
//     vault:
//       uri: https://vault.internal:8200
//       authentication: TOKEN
//       token: ${VAULT_TOKEN}

@Component
class DbConfig {
    // Value stored in Vault at secret/myapp under field "db.password"
    @Value("${db.password}")
    private String password;   // injected like any ordinary property
}

go deeper

for a junior

Know it's HashiCorp Vault + Spring, and that secrets become normal properties via a PropertySource.

for a middle

Explain the default path scheme (app-name/profile), auth methods, and spring.config.import=vault://.

for a senior

Contrast bootstrap vs Config Data loading, fail-fast, and why static secrets need refresh to update.

for a principal

Discuss auth strategy for prod (AppRole/Kubernetes), source ordering/precedence, and Vault availability as a startup dependency.

**HashiCorp Vault** is a dedicated secrets-management server: instead of putting a database password in `application.yml` or an env var, you store it in Vault, which handles encryption-at-rest, access policies, audit logging, and (for some engines) generating short-lived credentials on demand. **Spring Cloud Vault** is the Spring integration that makes those secrets appear as normal Spring properties. The core mechanism is Spring's `Environment` and its ordered list of `PropertySource` objects. Spring Cloud Vault contributes a `VaultPropertySource` (backed by `VaultTemplate`, the low-level Vault client) so that a key stored in Vault at path `secret/myapp` under field `db.password` becomes resolvable as the property `db.password`. Your code stays Vault-agnostic: `@Value("${db.password}")`, `@ConfigurationProperties`, or `environment.getProperty("db.password")` all work. **When it runs:** secrets must be present *before* other beans (like a `DataSource`) are created, so the property source is added very early. - **Legacy (bootstrap) style:** a separate *bootstrap ApplicationContext* runs before the main context; requires `spring-cloud-starter-bootstrap` or `spring.cloud.bootstrap.enabled=true`. A `VaultPropertySourceLocator` runs there. - **Modern (Config Data) style, Spring Boot 2.4+:** add `spring.config.import=vault://` (optionally `vault://secret/myapp`). A `VaultConfigDataLoader` imports secrets during the normal config-data phase — no bootstrap context needed. This is the recommended approach today. **Which paths are read:** by default Spring Cloud Vault reads the KV engine at `<backend>/<application-name>` plus profile-specific paths `<backend>/<application-name>/<profile>`, and a shared default application path. `spring.application.name` supplies the app name; `spring.cloud.vault.kv.default-context` (default `application`) supplies the shared path. **Authentication:** Vault requires you to authenticate before reading. Methods include `TOKEN` (simplest, dev), `APPROLE` (role-id + secret-id, common in prod), `KUBERNETES`, `AWS`, etc., set via `spring.cloud.vault.authentication` and related properties. **Minimal config:** ```yaml spring: application: name: myapp config: import: vault:// cloud: vault: uri: https://vault.internal:8200 authentication: TOKEN token: s.xxxxx ``` **Gotchas:** - If Vault is unreachable, startup behavior depends on `spring.cloud.vault.fail-fast` (true = fail startup, false = continue without those secrets). - Secrets are read at startup, so a value changed in Vault later is *not* picked up automatically for static KV secrets — you need `/actuator/refresh` (with `@RefreshScope`) or, for dynamic engines, lease renewal. - `@Value` on a normal singleton bean captures the value once at creation; to see refreshed values put the bean in `@RefreshScope`. - Property precedence matters: a value in `application.yml` may override or be overridden by Vault depending on source ordering.

  • Why can your beans inject Vault secrets without any Vault-specific code?
    Because Spring Cloud Vault registers the secrets as a standard `PropertySource` in the `Environment`. Property resolution (`@Value`, `@ConfigurationProperties`) is source-agnostic, so a Vault-backed property behaves identically to one from `application.yml`.
  • What changed between the bootstrap-context approach and the Config Data approach?
    The bootstrap approach ran a separate pre-main `ApplicationContext` and needed `spring-cloud-starter-bootstrap`. Spring Boot 2.4+ introduced the Config Data API: you use `spring.config.import=vault://` and secrets load in the normal config-data phase via `VaultConfigDataLoader`, no bootstrap context required.

context