PostgreSQL Dynamic Credentials with HashiCorp Vault

PostgreSQL Dynamic Credentials with HashiCorp Vault

7 October 2025

Kilian Niemegeerts

This is part 4 of our HashiCorp Vault production series. Remember those database passwords stored in your .env files? The ones that haven’t changed since 2019? Yeah, we need to talk.

Series overview:

  1. Production Kubernetes Architecture with HashiCorp Vault 
  2. Terraform Infrastructure for HashiCorp Vault on EKS 
  3. External Secrets Operator: GitOps for Kubernetes Secrets 
  4. Dynamic PostgreSQL Credentials with HashiCorp Vault 
  5. Vault Agent vs Secrets Operator vs CSI Provider 
  6. Securing Vault Access with Internal NLB and VPN

What is Vault Database Secrets Engine?

The Vault database secrets engine is a game-changer for database security. Instead of sharing that one DB_PASSWORD across your entire team (we’ve all been there), Vault generates unique, temporary credentials for each application or user.

Dynamic vs Static Database Credentials: Why It Matters

Traditional static credentials are like house keys,lose one, and you need to change all the locks. Dynamic credentials are like hotel keycards, each guest gets their own, and they expire automatically.

Benefits we’ve seen in production:

  • No more password rotation tickets
  • Automatic cleanup of unused accounts
  • Zero hardcoded passwords in applications in this setup
  • Complete traceability. You know exactly which app made which database call, essential for regulatory compliance

The shift from static to dynamic credentials fundamentally changes your security posture. Instead of defending long-lived secrets, you’re managing short-lived access that’s automatically cleaned up.

How Dynamic Database Credentials Work in Vault

Our setup uses Vault to dynamically create PostgreSQL users with temporary credentials. 

Here’s the flow:

The magic happens in four steps:

  1. Application requests credentials from Vault
  2. Vault creates a temporary PostgreSQL user with specific permissions
  3. Vault returns the credentials with a lease
  4. Application connects using these temporary credentials

Configuring Vault Database Secrets Engine for PostgreSQL

Enable the Database Engine

First, enable the database secrets engine in Vault:

 

vault secrets enable database

Configure PostgreSQL Connection

Set up the connection to your PostgreSQL database:

 

vault write database/config/my-postgres-db \
  plugin_name=postgresql-database-plugin \
  allowed_roles="app-role" \
  connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/mydb" \
  username="vaultadmin" \
  password="securepassword"

Define the Database Role

This is where you specify what the temporary users can do:

 

vault write database/roles/app-role \
  db_name=my-postgres-db \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
  default_ttl="1h" \
  max_ttl="24h"

Key settings in our production setup:

  • default_ttl: 1 hour (applications get fresh credentials hourly)
  • max_ttl: 24 hours (hard limit for any credential)

Integration with Kubernetes Applications

In Kubernetes, applications authenticate with Vault and receive their database credentials. Using the Vault Agent (covered in detail in part 5), credentials are injected as files into pods. The beauty? Your application code doesn’t change. It just reads database credentials from a file path, unaware they’re being dynamically generated and rotated.

What’s Next?

We’ve covered dynamic database credentials, but how do you get these credentials into your pods? That’s where Vault Agent comes in – our topic for the next post.

The complete configuration is available in our GitHub repository, including:

  • Full database engine setup
  • PostgreSQL role configurations
  • TTL best practices
  • Integration examples

Continue reading: Vault Agent vs Vault Secrets Operator

FAQ

Q: What is the default TTL for database credentials in the setup?

A: We use a default TTL of 1 hour (default_ttl=”1h”) with a maximum TTL of 24 hours (max_ttl=”24h”). This balances security with performance in production environments.

Q: Which PostgreSQL statement does Vault use to create temporary users?

A: Vault executes CREATE ROLE “{{name}}” WITH LOGIN PASSWORD ‘{{password}}’ VALID UNTIL ‘{{expiration}}’ to create temporary database users with an automatic expiration time.

Q: How does Vault connect to the PostgreSQL database?

A: Through a connection URL in the format postgresql://{{username}}:{{password}}@db.example.com:5432/mydb using a dedicated vaultadmin user with appropriate privileges.

Q: What is a lease in the context of Vault database credentials?

A: A lease is Vault’s mechanism for tracking temporary credentials. When an application requests database credentials, it receives them with a lease that determines how long they’re valid and when they’ll be automatically revoked.

Q: Does the database secrets engine store passwords permanently?

A: No, Vault never stores passwords permanently. Credentials are generated on-demand when requested and automatically expire after their TTL period.

Q: Why use 1-hour TTL instead of shorter or longer periods?

A: The 1-hour TTL is our sweet spot in production – short enough to limit exposure if credentials leak, but long enough to avoid performance issues from constant user creation/deletion operations.

No Comments

Sorry, the comment form is closed at this time.