Dynamic database credentials for Jenkins with Vault
Stop pasting Postgres passwords into Jenkins credentials. Vault mints a fresh user per build and revokes it when the job ends.
A static Postgres password pasted into Jenkins credentials is a secret with no expiry, shared by every build on every branch, visible to anyone with Job/Configure permission, and sitting in a heap dump the first time a pipeline crashes with a core file. Rotating it is a quarterly fire drill that breaks in-flight jobs. Vault's database secrets engine replaces the pattern entirely: Jenkins asks Vault for credentials at job start, Vault runs CREATE ROLE against Postgres with a short TTL, the build uses a unique username, and Vault drops the role when the lease expires — or when you explicitly revoke it on job completion.
The admin connection Vault uses to mint users is powerful — treat it like a break-glass credential with allowed_roles restricting which roles it can create. Keep TTL just longer than your slowest migration job, not hours. Pair with the HashiCorp Vault Jenkins plugin so credentials never touch the Jenkins credential store or disk. The Vault from dev to production track covers the database engine, policies, and CI integration patterns.
A crashed build still holds a lease until TTL expires. Revoke on completion or keep TTL aggressively short.
Enable the database secrets engine
Point Vault at your database with a dedicated admin user Vault uses only to create and drop application roles. The allowed_roles list on the connection config is the guardrail — this connection cannot mint arbitrary roles outside that list even if someone misconfigures a policy.
vault secrets enable databasevault write database/config/appdb \plugin_name=postgresql-database-plugin \allowed_roles="jenkins-ci" \connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/app?sslmode=require" \username="vault_admin" \password="$ADMIN_PW"
A role that mints short-lived users
The role defines the SQL Vault executes to create a user and the TTL before automatic revocation. Grant only what migrations need — SELECT, INSERT, UPDATE, DELETE on application tables, not SUPERUSER. Use VALID UNTIL in creation statements so Postgres enforces expiry even if Vault's revocation path fails.
vault write database/roles/jenkins-ci \db_name=appdb \default_ttl="10m" max_ttl="20m" \creation_statements="\CREATE ROLE \"{{name}}\" LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
Read the role once from a test shell and confirm Vault returns a unique username, password, and lease metadata before you wire Jenkins.
vault read database/creds/jenkins-ciKey Value--- -----lease_id database/creds/jenkins-ci/8Kx2..lease_duration 10musername v-jenkins-ci-6Yh0password A1a-9f3Kp2mZ-qX7user auto-dropped when the 10m lease expiresWire it into the pipeline
The HashiCorp Vault plugin's withVault block injects secrets as environment variables scoped to a single stage. They never enter the Jenkins credential store, never appear in job config XML, and disappear when the block exits. Authenticate Jenkins to Vault with AppRole or JWT — not a root token in an environment variable on the controller.
withVault(vaultSecrets: [[path: 'database/creds/jenkins-ci',secretValues: [[envVar: 'DB_USER', vaultKey: 'username'],[envVar: 'DB_PASS', vaultKey: 'password'],]]]) {sh './run-migrations.sh' // DB_USER / DB_PASS live only in this block}
Least-privilege Vault policy for Jenkins
Jenkins needs read on exactly one path — database/creds/jenkins-ci — and optionally update on that path if you use renew. It does not need access to KV, PKI, or other database roles. Separate AppRoles per Jenkins controller or per folder so a compromised dev job cannot read production credentials.
path "database/creds/jenkins-ci" {capabilities = ["read"]}# no path "database/creds/*" — scope to one role
Where this goes next
The same pattern extends to Vault's AWS secrets engine for deployment jobs, PKI for short-lived TLS in integration tests, and SSH OTP for ephemeral bastion access. Once builds stop carrying long-lived secrets, credential rotation becomes continuous instead of quarterly. The Vault from dev to production path covers dynamic secrets, auth methods for CI, and operating Vault in production.
Go deeper in a courseVault from dev to productionDatabase engine, dynamic secrets, CI auth, and rotation without fire drills.View course