When to use this guide
- HashiCorp Vault is your organizational standard.
- Vault Agent Injector is available in Kubernetes.
- Vault Secrets Operator (VSO) is deployed in the cluster.
- The application integrates directly through Spring Cloud Vault.
- A Vault token is hard-coded in
application.yml.
Examples
Pattern A: Spring Cloud Vault (direct integration)
The application calls the Vault API to load secrets. Spring Cloud Vault reads spring.config.import: vault:// in application.yml and supplies values from Vault.
When to choose this pattern
- Vault is also used outside Kubernetes, such as on VMs or ECS.
- The Spring Boot application needs to retrieve secrets directly from Vault.
Before
# application.yml: 토큰 하드코딩
spring:
config:
import: vault://
cloud:
vault:
host: vault.internal
port: 8200
scheme: https
authentication: TOKEN
token: hvs.xxxxxxxxxxxxxxxxxxxxxxxxxxxx # 절대 커밋하면 안되는 부분
kv:
enabled: true
backend: secret
default-context: my-service
After
# application.yml: Kubernetes Auth + role 기반 인증
spring:
config:
import: vault://
cloud:
vault:
host: vault.internal
port: 8200
scheme: https
authentication: KUBERNETES
kubernetes:
role: my-service-role
service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token
kv:
enabled: true
backend: secret
default-context: my-service
In Kubernetes, KUBERNETES authentication uses a service account token. On VMs or ECS, consider supported platform authentication or APPROLE; deliver AppRole's role-id and secret-id securely. For a separately mounted projected service account token, set service-account-token-file to its actual path, such as /var/run/secrets/tokens/vault-token.
Dependency examples
// build.gradle
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'
// Dynamic Secrets (Database Secret Engine) 사용 시 추가
implementation 'org.springframework.cloud:spring-cloud-vault-config-databases'
}
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
<!-- Dynamic Secrets (Database Secret Engine) 사용 시 추가 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-databases</artifactId>
</dependency>
Spring code examples
Vault keys become property names, so names such as db.password and jwt.secret must match the application's references. The @ConfigurationProperties class below needs a validation dependency and registration through @ConfigurationPropertiesScan or @EnableConfigurationProperties.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class DatabaseConfig {
// Vault 경로 secret/my-service의 db.password 키에 저장된 값
@Value("${db.password}")
private String dbPassword;
@Value("${jwt.secret}")
private String jwtSecret;
}
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;
@Validated
@ConfigurationProperties(prefix = "db")
public class DbProperties {
@NotBlank
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Pattern B: Vault Agent Injector (Kubernetes sidecar)
Vault Agent runs as an injected sidecar and supplies secrets as files. With the Injector and Vault authentication configured, annotations request file injection and Spring reads the file through spring.config.import.
When to choose this pattern
- Vault Agent Injector is deployed in the Kubernetes cluster.
- You want to inject Vault secrets as files without changing application code.
Before
# Vault 토큰 하드코딩 및 DB 비밀번호와의 혼동
env:
- name: VAULT_TOKEN
value: "hvs.xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret # DB 비밀번호 대신 Vault 토큰을 참조하는 잘못된 예시
key: vault-token
After
Pod annotation example
This Deployment excerpt omits selectors, labels, and other settings. Bind the Pod's service account to the Vault role and grant read access to the required path.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-service-role"
vault.hashicorp.com/agent-inject-secret-application.properties: "secret/data/my-service"
vault.hashicorp.com/agent-inject-template-application.properties: |
{{- with secret "secret/data/my-service" -}}
db.password={{ .Data.data.db_password }}
jwt.secret={{ .Data.data.jwt_secret }}
{{- end }}
spec:
containers:
- name: my-service
image: my-service:latest
The Injector creates /vault/secrets/application.properties. The template maps KV v2 keys db_password and jwt_secret to Spring properties. Escape line breaks and special characters in actual values for the properties format.
How Spring reads the file
# application.yml: 주입된 파일을 Spring 설정으로 로딩
spring:
config:
import: file:/vault/secrets/application.properties
The secret file is required, so the import does not use optional:.
Pattern C: Vault Secrets Operator
VSO watches VaultStaticSecret and VaultDynamicSecret resources and synchronizes Vault values into Kubernetes Secrets. The application reads ordinary Kubernetes Secrets without a Vault integration. Since the values are then stored in etcd outside Vault's encryption boundary, configure encryption at rest and RBAC first.
When to choose this pattern
- VSO is deployed in Kubernetes.
- You want to use Kubernetes Secrets without a Vault Agent sidecar.
- The same secret needs to be supplied to multiple namespaces.
Before
# Kubernetes Secret에 값을 직접 하드코딩하지 않는다
apiVersion: v1
kind: Secret
metadata:
name: my-service-secret
type: Opaque
stringData:
db-password: "s3cr3t-password" # 절대 커밋하면 안되는 부분
jwt-secret: "my-jwt-secret-key"
After
# VaultStaticSecret CRD: VSO가 Vault에서 값을 읽어 Kubernetes Secret으로 동기화
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: my-service-secret
namespace: my-namespace
spec:
vaultAuthRef: my-vault-auth
mount: secret
type: kv-v2
path: my-service
refreshAfter: 60s
destination:
name: my-service-secret # 생성될 Kubernetes Secret 이름
create: true
How Spring reads the values
The resource references my-vault-auth, configured in the same namespace. If the Vault keys are db_password and jwt_secret, explicitly map them to environment variables and application properties:
# application.yml
db:
password: ${DB_PASSWORD}
jwt:
secret: ${JWT_SECRET}
# 컨테이너 환경 변수 설정의 일부
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-service-secret
key: db_password
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: my-service-secret
key: jwt_secret
Environment variables in a running process do not refresh automatically. Configure a restart or supported reload procedure after synchronization.
Dynamic secrets
Vault can issue DB accounts or AWS keys with a lease. Monitor renewal and revocation, and configure the application to adopt new credentials.
Use with Pattern A (Spring Cloud Vault)
# application.yml: DB Dynamic Secrets
spring:
config:
import: vault://
cloud:
vault:
host: vault.internal
port: 8200
scheme: https
authentication: KUBERNETES
kubernetes:
role: my-service-role
service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token
database:
enabled: true
role: my-service-db-role # Vault Database Secret Engine에 등록된 role
backend: database
Spring Cloud Vault obtains database credentials at startup and supplies them to the DataSource. It manages renewable leases, but does not obtain new credentials and reconfigure the DataSource at max_ttl. Arrange an application restart or separate credential and connection-pool refresh before expiry.
Use with Pattern C (VSO)
# VaultDynamicSecret CRD
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
name: my-service-db-secret
namespace: my-namespace
spec:
vaultAuthRef: my-vault-auth
mount: database
path: creds/my-service-db-role
destination:
name: my-service-db-secret
create: true
Developer tasks
- Remove Vault tokens from
application.yml. - Pattern A: Choose a supported authentication method for the environment and protect credential delivery.
- Pattern B: Add Pod annotations and import the injected file with
spring.config.import. - Pattern C: Define
VaultStaticSecretorVaultDynamicSecretresources. - Validate required
@ConfigurationPropertiesvalues with constraints such as@NotBlankand fail closed when missing. - Check logs, exceptions, and Actuator endpoints for secret exposure.
Infrastructure and platform tasks
- Create service-specific Vault policies and roles with least privilege.
- Enable the Kubernetes auth method and bind each service account to the appropriate role.
- Pattern B: Confirm that
vault-agent-injectoris deployed. - Pattern C: Confirm that VSO is deployed.
- Configure database connections and roles in the Database Secrets Engine.
- Document restarts or reloads for secret rotation.
- Keep Vault tokens out of Helm values, ConfigMaps, and Docker images.
Verification
- Check
application.yml, Helm values, and ConfigMaps for embedded Vault tokens. - Pattern A: Confirm successful loading at startup without logging secret values.
- Pattern B: Check that the file exists under
/vault/secrets/withkubectl exec. - Pattern C: Confirm creation of the synchronized Secret with
kubectl get secret. - Check
/actuator/env, debug logs, and exception responses for exposure. - Confirm that a missing required secret prevents startup.
Common mistakes
- Hard-coding a Vault token in
application.yml - Using a long-lived token without access controls or rotation
- Hard-coding Vault paths in application code instead of configuration
- Omitting restart procedures after rotation
- Failing to coordinate lease renewal, maximum TTL, and the application's adoption of new credentials
Example instructions for the owner
- “Remove the Vault token from
application.ymland useKUBERNETESauthentication in Kubernetes.” - “Configure Vault authentication and the Injector, request file injection through annotations, and import the required file with
spring.config.import.” - “Use authentication suited to the environment and least privilege, and document how the application adopts rotated secrets.”
- “With VSO deployed, define
VaultStaticSecretorVaultDynamicSecretresources to supply Vault values as ordinary Kubernetes Secrets.” - “For dynamic secrets, check renewal and maximum TTL, and ensure the application uses new credentials before expiry.”