Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for HashiCorp Vault By James Joyner IV · · 9 min read Last reviewed Jul 2026

Vault Error: 'error creating database object' Database Secrets Engine Connection Failed

Quick answer

Fix Vault database secrets engine connection failures: correct plugin_name, connection_url templating, allowed_roles, root credential rotation, TLS trust, and network reachability.

  • #vault
  • #secrets
  • #security-hardening
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this HashiCorp Vault error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Exact Error Message

Error writing data to database/config/app-postgres: Error making API request.

URL: PUT https://vault.example.com:8200/v1/database/config/app-postgres
Code: 400. Errors:

* error creating database object: error verifying connection: failed to connect to
  `host=db.internal user=vault database=appdb`: dial error
  (dial tcp 10.0.4.12:5432: connect: connection refused)

On credential issuance you may instead see:

Error reading database/creds/app-readonly: Error making API request.

URL: GET https://vault.example.com:8200/v1/database/creds/app-readonly
Code: 500. Errors:

* 1 error occurred:
	* failed to create user: pq: password authentication failed for user "vault"

What It Means

The database secrets engine does not proxy your application’s queries — it holds one privileged root connection to the database and uses it to create short-lived, per-request users. Writing database/config/<name> opens that root connection immediately and verifies it, which is why the write itself fails rather than failing later at credential issuance. The error you get back is the driver’s own error, wrapped by Vault, so connection refused, password authentication failed, and x509: certificate signed by unknown authority all mean exactly what they would mean from psql on the same host.

The second shape — a config that writes cleanly but fails on database/creds/<role> — means the connection is fine but the root user cannot execute the role’s creation_statements. Typically the root user lacks CREATEROLE, or the statements reference a schema or grant that does not exist. Vault reports these as 500s because they occur inside the plugin’s execution of your SQL, not in request validation.

Common Causes

  • The Vault server (not your laptop) cannot reach the database host or port — security group, firewall, or network policy.
  • connection_url has the credentials hardcoded instead of using the {{username}}/{{password}} templating, so root rotation silently breaks it.
  • The root credentials are wrong, or were rotated out-of-band by a DBA after Vault took ownership.
  • The database enforces TLS and the Vault host does not trust the database’s CA.
  • allowed_roles does not include the role you are reading, so issuance is refused even with a healthy connection.
  • The plugin is not registered or plugin_name is misspelled — postgresql-database-plugin, not postgres.
  • max_open_connections is set so low that concurrent issuance starves, producing intermittent timeouts.

Diagnostic Commands

Confirm the secrets engine is actually mounted and at the path you think:

vault secrets list -detailed | grep -i database

List the registered database plugins in the catalog. A missing plugin here is the fastest explanation for a config write that fails immediately:

vault read sys/plugins/catalog/database
vault read sys/plugins/catalog/database/postgresql-database-plugin

Read the current config. Note that Vault never returns the password — only the templated URL and role allowlist:

vault read database/config/app-postgres

Test reachability from the Vault server, since that is the host that dials the database:

nc -vz db.internal 5432
getent hosts db.internal

Test the root credential path directly with the database client, which separates network problems from auth problems:

PGPASSWORD='...' psql -h db.internal -U vault -d appdb -c 'select current_user, version();'

Watch Vault’s own log while retrying the write — the plugin logs the driver error with more context than the API response:

journalctl -u vault -f | grep -iE "database|plugin|connection"

Finally, confirm your token can even perform the operation, since a policy gap returns a 403 that is easy to confuse with a backend failure:

vault token capabilities "$(vault print token)" database/creds/app-readonly

Step-by-Step Resolution

  1. Register the plugin if it is missing from the catalog. Built-in plugins are present by default; only external builds need explicit registration:
vault write sys/plugins/catalog/database/custom-postgres-plugin \
  sha256="$(sha256sum /etc/vault.d/plugins/custom-postgres | cut -d' ' -f1)" \
  command="custom-postgres"
  1. Write the connection config with templated credentials. The {{username}} and {{password}} placeholders are mandatory if you ever want root rotation to work — hardcoding them produces a config that breaks the moment you rotate:
vault write database/config/app-postgres \
  plugin_name="postgresql-database-plugin" \
  allowed_roles="app-readonly,app-readwrite" \
  connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb?sslmode=verify-full" \
  username="vault" \
  password="$ROOT_PW" \
  max_open_connections=8 \
  max_idle_connections=4 \
  max_connection_lifetime="5m"
  1. If the failure is TLS trust, install the database’s CA on the Vault host rather than weakening the connection string. sslmode=disable and verify_connection=false are diagnostics only — see step 4:
sudo cp db-ca.pem /usr/local/share/ca-certificates/db-ca.crt
sudo update-ca-certificates
sudo systemctl restart vault
  1. Diagnostic only: to prove that TLS is the failing layer and nothing else, you can write the config once with verification off. This stores a config that does not validate the database’s identity, so restore proper CA trust and rewrite the config immediately afterwards:
vault write database/config/app-postgres \
  plugin_name="postgresql-database-plugin" \
  allowed_roles="app-readonly" \
  connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb?sslmode=disable" \
  username="vault" password="$ROOT_PW" \
  verify_connection=false

If that write succeeds where the verified one failed, the problem is CA trust, not credentials or networking. Go back to step 3.

  1. Rotate the root credential so only Vault knows it. Do this once, deliberately — the new password is never returned, and the old one stops working immediately:
vault write -f database/rotate-root/app-postgres
vault read database/config/app-postgres
  1. Define the role with creation_statements the root user is actually privileged to run, then issue a credential to prove the whole path works:
vault write database/roles/app-readonly \
  db_name="app-postgres" \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

vault read database/creds/app-readonly

If this returns password authentication failed or a permissions error on CREATE ROLE, grant the root user CREATEROLE in the database and retry — Vault cannot mint users it is not allowed to create.

Prevention

  • Always template {{username}}/{{password}} in connection_url and rotate root immediately after the first successful write.
  • Keep allowed_roles explicit rather than *, so a new role cannot accidentally borrow a privileged connection.
  • Set max_ttl and default_ttl deliberately; long-lived dynamic credentials defeat the purpose of the engine and pile up orphaned database roles.
  • Size max_open_connections against the database’s own connection limit, leaving headroom for the application’s pool.
  • Monitor lease counts and revocation failures — undeleted roles from failed revocations accumulate silently.
  • Run a synthetic vault read database/creds/<role> in monitoring so a broken root credential is detected before an application deploy hits it.

Frequently Asked Questions

Why did the config write fail instead of just being stored? Vault verifies the connection at write time by default. This is deliberate — it surfaces a broken config at the moment you create it rather than during a production credential request. verify_connection=false skips that check but does not make the connection work.

Is verify_connection=false ever a legitimate long-term setting? Rarely. It is useful when the database is genuinely not reachable yet during bootstrapping, for example when Vault is configured before the database is provisioned. Outside that case it just hides a real failure until the first database/creds read.

What happens if a DBA changes the root password after rotation? Vault’s stored copy becomes stale and every issuance fails. Recover by writing the config again with the new password and immediately running vault write -f database/rotate-root/<name> so Vault is once again the only holder.

Do dynamic credentials get cleaned up if Vault is down? Leases are tracked by Vault, so revocation happens when it comes back, not while it is down. If a node was down past max_ttl you may need vault lease revoke -prefix database/creds/ to force cleanup. More engine-specific fixes live in the Vault guides.

Free download · 368-page PDF

Fixed it? Get 500 HashiCorp Vault & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.