HashiCorp Vault — Agent Templating
Vault Agent’s templating feature renders secrets into configuration files using Go’s text/template syntax (via Consul Template). Applications read plain files instead of calling the Vault API.
How Templating Works
1. Agent authenticates to Vault (auto-auth)
2. Agent reads template files with secret references
3. Agent fetches secrets from Vault
4. Agent renders secrets into destination files
5. Agent optionally runs a command (e.g., reload app)
6. Agent watches for secret changes and re-renders
Basic Template Configuration
# vault-agent.hcl
vault {
address = "http://127.0.0.1:8200"
}
auto_auth {
method "approle" {
config = {
role_id_file_path = "/etc/vault/role-id"
secret_id_file_path = "/etc/vault/secret-id"
}
}
}
template {
source = "/etc/vault/templates/app.env.tpl"
destination = "/etc/app/app.env"
perms = 0640
command = "systemctl reload myapp"
}
Template File
# /etc/vault/templates/app.env.tpl
{{ with secret "secret/data/myapp/config" }}
DB_HOST={{ .Data.data.db_host }}
DB_PORT={{ .Data.data.db_port }}
DB_USER={{ .Data.data.db_user }}
DB_PASS={{ .Data.data.db_pass }}
{{ end }}
Rendered output (/etc/app/app.env):
DB_HOST=db.example.com
DB_PORT=5432
DB_USER=myapp
DB_PASS=super-secret-password
Template Syntax
Read KV v2 Secrets
{{ with secret "secret/data/myapp/config" }}
USERNAME={{ .Data.data.username }}
PASSWORD={{ .Data.data.password }}
{{ end }}
Note: KV v2 uses
secret/data/PATHand.Data.data.KEY(double data).
Read KV v1 Secrets
{{ with secret "kv/myapp/config" }}
USERNAME={{ .Data.username }}
PASSWORD={{ .Data.password }}
{{ end }}
Read Dynamic Secrets
{{ with secret "database/creds/my-role" }}
DB_USER={{ .Data.username }}
DB_PASS={{ .Data.password }}
{{ end }}
Read PKI Certificates
{{ with secret "pki_int/issue/web-server" "common_name=api.example.com" "ttl=24h" }}
{{ .Data.certificate }}
{{ .Data.issuing_ca }}
{{ end }}
Conditional Logic
{{ with secret "secret/data/myapp/config" }}
{{ if .Data.data.debug_enabled }}
DEBUG=true
LOG_LEVEL=debug
{{ else }}
DEBUG=false
LOG_LEVEL=info
{{ end }}
{{ end }}
Loops
{{ range secrets "secret/metadata/myapp/" }}
# Secret: {{ . }}
{{ with secret (printf "secret/data/myapp/%s" .) }}
{{ range $key, $value := .Data.data }}
{{ $key }}={{ $value }}
{{ end }}
{{ end }}
{{ end }}
Multiple Templates
# Render database config
template {
source = "/etc/vault/templates/db.conf.tpl"
destination = "/etc/myapp/db.conf"
command = "systemctl reload myapp"
}
# Render TLS certificates
template {
source = "/etc/vault/templates/cert.tpl"
destination = "/etc/myapp/tls/cert.pem"
command = "systemctl reload nginx"
}
template {
source = "/etc/vault/templates/key.tpl"
destination = "/etc/myapp/tls/key.pem"
command = "systemctl reload nginx"
}
# Render as JSON
template {
source = "/etc/vault/templates/config.json.tpl"
destination = "/etc/myapp/config.json"
}
JSON Config Template
# /etc/vault/templates/config.json.tpl
{{ with secret "secret/data/myapp/config" }}
{
"database": {
"host": "{{ .Data.data.db_host }}",
"port": {{ .Data.data.db_port }},
"username": "{{ .Data.data.db_user }}",
"password": "{{ .Data.data.db_pass }}"
},
"redis": {
"url": "{{ .Data.data.redis_url }}"
}
}
{{ end }}
Inline Templates
For short templates, use inline content instead of a file:
template {
contents = "{{ with secret \"secret/data/myapp/config\" }}DB_PASS={{ .Data.data.db_pass }}{{ end }}"
destination = "/etc/myapp/db-password"
perms = 0600
}
Template Options
| Option | Description |
|---|---|
source |
Path to the template file |
destination |
Path to write the rendered output |
contents |
Inline template (alternative to source) |
command |
Command to run after rendering |
perms |
File permissions for destination |
error_on_missing_key |
Fail if a template key is missing |
exec |
Execute a child process with the rendered environment |
Quick Reference
| Pattern | Template Syntax |
|---|---|
| KV v2 read | {{ with secret "secret/data/PATH" }}{{ .Data.data.KEY }}{{ end }} |
| KV v1 read | {{ with secret "kv/PATH" }}{{ .Data.KEY }}{{ end }} |
| Dynamic secret | {{ with secret "database/creds/ROLE" }}{{ .Data.username }}{{ end }} |
| PKI cert | {{ with secret "pki_int/issue/ROLE" "common_name=CN" }}{{ .Data.certificate }}{{ end }} |
| List secrets | {{ range secrets "secret/metadata/PATH/" }}{{ . }}{{ end }} |
Next: Sidecar Pattern