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: 'x509: certificate signed by unknown authority' on TLS Connect

Quick answer

Fix Vault's x509 'certificate signed by unknown authority' error: set VAULT_CACERT, install the CA in the system trust store, and serve the full intermediate chain.

  • #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

$ vault status
Error checking seal status: Get "https://vault.example.com:8200/v1/sys/seal-status":
x509: certificate signed by unknown authority

On macOS and some minimal container images the same failure surfaces with a slightly different wrapper:

Error making API request.

URL: GET https://vault.example.com:8200/v1/sys/health
Code: -1. Errors:

* Get "https://vault.example.com:8200/v1/sys/health": tls: failed to verify
  certificate: x509: certificate signed by unknown authority

What It Means

The Vault client opened a TCP connection and completed the TLS handshake far enough to receive the server’s certificate, then refused to trust it. Go’s TLS stack — which the vault CLI, the Go SDK, and most Vault-aware agents use — verifies the presented chain against a trust store. If it cannot build a path from the leaf certificate up to a root it already trusts, verification fails with exactly this message. Nothing about the request itself reached Vault’s API; the error is entirely client-side trust evaluation.

There are only two ways this happens. Either the client does not have the issuing CA in its trust store, or the server is not sending the intermediate certificates needed to link its leaf to that CA. The second case is deceptive: a browser may load the same URL fine because browsers cache intermediates from previous sessions and can fetch them via AIA, while Go does neither. So “it works in Chrome” is not evidence that the chain is complete — it usually means the chain is incomplete and something else papered over it.

Common Causes

  • Vault’s listener uses a certificate from an internal or self-signed CA that the client machine has never been told to trust.
  • tls_cert_file contains only the leaf certificate, with the intermediate CA certificates missing from the bundle.
  • VAULT_CACERT is unset, points at a path that does not exist, or points at the leaf instead of the CA.
  • The CA was installed into the system trust store but update-ca-trust / update-ca-certificates was never run.
  • A container image (especially scratch, distroless, or alpine without ca-certificates) has no trust store at all.
  • The listener cert was issued by Vault’s own PKI engine, and that PKI mount’s CA was never distributed to clients.
  • A TLS-terminating proxy or corporate MITM appliance sits in front of Vault and presents its own certificate.

Diagnostic Commands

See exactly what the server sends, including whether intermediates are present:

openssl s_client -showcerts -connect vault.example.com:8200 -servername vault.example.com </dev/null

Count the certificates in the output. A correctly configured listener sends the leaf plus every intermediate up to (but not necessarily including) the root. If you see exactly one BEGIN CERTIFICATE block and the issuer is not a public root, your chain is incomplete.

Compare the issuer of the leaf with the subject of what you are trusting:

openssl x509 -in /etc/vault.d/tls/vault.crt -noout -subject -issuer -dates
openssl x509 -in /etc/vault.d/tls/ca.crt -noout -subject -dates

Verify the chain locally with the CA you intend to distribute:

openssl verify -CAfile /etc/vault.d/tls/ca.crt /etc/vault.d/tls/vault.crt

Check what the client environment is actually configured with:

env | grep -E 'VAULT_ADDR|VAULT_CACERT|VAULT_CAPATH|VAULT_CLIENT_CERT'

Inspect the Subject Alternative Names, since a hostname problem produces a different x509 error and needs a different fix:

openssl x509 -in /etc/vault.d/tls/vault.crt -noout -ext subjectAltName

Step-by-Step Resolution

  1. Obtain the issuing CA certificate. If Vault’s PKI secrets engine issued the listener certificate, pull the CA chain straight from the mount:
curl -s https://vault.example.com:8200/v1/pki/ca/pem -o /etc/vault.d/tls/ca.crt
# or, once you can authenticate:
vault read -field=certificate pki/cert/ca > /etc/vault.d/tls/ca.crt
  1. Point the client at that CA. For a single file use VAULT_CACERT; for a directory of PEM files use VAULT_CAPATH:
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_CACERT=/etc/vault.d/tls/ca.crt
vault status
  1. If many clients need it, install the CA into the system trust store instead of setting an env var everywhere. On Debian/Ubuntu:
sudo cp ca.crt /usr/local/share/ca-certificates/internal-vault-ca.crt
sudo update-ca-certificates

On RHEL, Rocky, or Fedora:

sudo cp ca.crt /etc/pki/ca-trust/source/anchors/internal-vault-ca.crt
sudo update-ca-trust extract
  1. Fix the server side if openssl s_client -showcerts returned only one certificate. Vault’s tls_cert_file must be the leaf plus all intermediates concatenated, leaf first — Vault does not assemble the chain for you:
cat vault-leaf.crt intermediate-ca.crt > /etc/vault.d/tls/vault-fullchain.crt
listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/etc/vault.d/tls/vault-fullchain.crt"
  tls_key_file  = "/etc/vault.d/tls/vault.key"
}

Reload Vault to pick up the new bundle — recent versions re-read the certificate files on SIGHUP without dropping the seal state:

sudo systemctl reload vault
openssl s_client -showcerts -connect vault.example.com:8200 </dev/null | grep -c 'BEGIN CERTIFICATE'
  1. Temporary diagnostic only. If you need to prove that TLS trust — and nothing else — is the blocker, you can bypass verification for a single command. This sends your token over a connection you have not authenticated, so it must never appear in production, in CI, in a Dockerfile, or in any shell profile. Use it once, in an interactive shell, then unset it:
VAULT_SKIP_VERIFY=true vault status   # DIAGNOSTIC ONLY - never commit or export this

If this succeeds while the verified call fails, the chain or trust store is confirmed as the sole problem. Go back to steps 1–4 and fix it properly; do not leave the flag in place.

  1. Confirm the fix from a clean environment, so a leftover variable is not masking anything:
env -u VAULT_SKIP_VERIFY -u VAULT_CACERT vault status

If this passes, the system trust store is correct and every tool on the host — not just the vault CLI — will trust the endpoint.

For generating listener stanzas, PKI role definitions, and chain-assembly steps matched to your CA layout, the Vault prompts in the prompt library can produce a reviewable configuration.

Prevention

  • Distribute the internal CA through configuration management or a base image layer so no host is ever missing it.
  • Build tls_cert_file from a fullchain artifact in your issuance pipeline, never from the bare leaf.
  • Add a post-issuance check that runs openssl s_client -showcerts and asserts the expected certificate count.
  • Include ca-certificates in any container image that talks to Vault, and bake the internal CA into the image.
  • Ban VAULT_SKIP_VERIFY in CI by failing the pipeline if the variable is set anywhere in the environment.
  • Monitor certificate expiry on the listener; a renewal that drops intermediates reproduces this error at the worst moment.
  • x509: certificate is valid for vault-1.internal, not vault.example.com — a SAN/hostname mismatch, fixed by reissuing with the right names, not by adding a CA.
  • x509: certificate has expired or is not yet valid — the chain is trusted but the validity window is wrong; check clock skew too.
  • tls: bad certificate — usually a client-certificate (mTLS) failure rather than a server-trust failure.
  • connection refused on /v1/sys/seal-status — nothing is listening, so TLS never starts.

Frequently Asked Questions

Why does curl work but vault status fail? curl on many distributions uses the system trust store while the Vault CLI may be running with a VAULT_CACERT that points somewhere else. Unset VAULT_CACERT and retry to see which store is actually being consulted.

Do I need the root CA or the intermediate on the client? The client needs the trust anchor — normally the root. The server is responsible for sending the intermediates. Trusting an intermediate directly works but breaks the moment that intermediate is rotated.

Is VAULT_SKIP_VERIFY ever acceptable? Only as a throwaway interactive diagnostic to confirm the failure is trust-related, as in step 5. It disables the protection that makes Vault’s transport safe, so any long-lived use — CI, systemd units, container env, shell profiles — is a security defect, not a workaround.

Vault’s PKI engine issues my listener cert — is that a chicken-and-egg problem? Somewhat: you need to reach Vault to get the CA, so fetch it from the unauthenticated /v1/pki/ca/pem endpoint or from your bootstrap artifacts. If authentication itself is failing afterwards, see Vault error: “missing client token” and the wider 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.