← Back to Blog
Security June 2026 7 min read

HashiCorp Vault PKI: Automating Certificate Lifecycle

Manually renewing internal TLS certificates doesn't scale past a handful of services. Vault's PKI secrets engine turns certificate issuance into an API call with a built-in expiry.

Why internal CAs usually rot

Most orgs start internal mTLS or service-to-service TLS with a manually managed CA — a root key on someone's laptop or a shared keystore, certs issued by hand, tracked in a spreadsheet. This works for the first ten services. By service fifty, nobody remembers which certs expire when, and "the payments service stopped accepting connections at 2am" turns out to be an expired cert nobody rotated.

Vault's PKI secrets engine replaces this with a programmatic CA: define roles with allowed domains and max TTLs, and any service with the right Vault policy can request a short-lived certificate on demand — no human in the loop, no spreadsheet.

Setting up a root and intermediate CA

Best practice is a root CA that's generated once, used only to sign an intermediate, and then taken offline (or never materialised in Vault at all — many teams keep the root entirely outside Vault and only put the intermediate in). The intermediate is what actually issues leaf certificates day to day.

vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int

vault write -field=csr pki_int/intermediate/generate/internal \
  common_name="internal.example.com Intermediate Authority" \
  > pki_intermediate.csr

# Sign the CSR with your root CA (in Vault or externally), then:
vault write pki_int/intermediate/set-signed certificate=@signed_intermediate.pem
✓ Keeping the root CA's private key out of any always-running system (or in a separate, rarely-unsealed Vault instance) limits blast radius if the operational Vault cluster is ever compromised.

Defining roles for issuance

A role constrains what a certificate request can actually ask for — allowed domains, whether subdomains/wildcards are permitted, and critically, the max TTL. Short TTLs are the entire point: a leaked short-lived cert is a much smaller problem than a leaked five-year one.

vault write pki_int/roles/internal-services \
  allowed_domains="svc.internal.example.com" \
  allow_subdomains=true \
  max_ttl="720h"

Issuing a certificate

A service (or more typically, a sidecar/init container) authenticates to Vault — usually via the Kubernetes auth method — and requests a cert for its own identity:

vault write pki_int/issue/internal-services \
  common_name="payments.svc.internal.example.com" \
  ttl="24h"

The response includes the certificate, private key, and the CA chain — ready to drop straight into a TLS listener config. Automating this at pod startup (via Vault Agent's template rendering, or a Kubernetes operator like cert-manager's Vault issuer) means certificates rotate automatically well before expiry, with zero manual renewal.

cert-manager + Vault: the Kubernetes-native pattern

In Kubernetes, cert-manager's Vault issuer is the common integration point: define an Issuer pointing at your PKI role, and cert-manager handles requesting, renewing, and storing certs as Kubernetes Secrets automatically, triggering renewal well ahead of expiry.

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: vault-issuer
spec:
  vault:
    server: https://vault.internal:8200
    path: pki_int/sign/internal-services
    auth:
      kubernetes:
        role: cert-manager
        mountPath: /v1/auth/kubernetes

Revocation and CRL/OCSP

Short TTLs reduce how much revocation matters in practice, but Vault still supports explicit revocation and publishes a CRL (Certificate Revocation List) that your services should be configured to check — particularly important for any certs issued with a longer TTL for external-facing use cases.

🚫 Don't set max_ttl higher than you actually need "to be safe." Long-lived certs reintroduce the exact rotation and revocation problem PKI automation is meant to solve.

Final thoughts

The PKI secrets engine turns TLS certificate management from a periodic manual chore with real outage risk into an automated, API-driven process with short-lived credentials as the default. Once it's wired into your Kubernetes auth flow and cert-manager (or an equivalent), certificate expiry stops being a pager-worthy event.