VaultCertsViewer : a lightweight dashboard to track certificate health

Tracking TLS certificates distributed across multiple Vault PKI engines can quickly become tedious: forgotten expirations, emergency renewals, lack of visibility.

VCV (Vault Certificate Viewer) is a lightweight, self-hosted web application that provides a clear and fast view of your certificates issued by Vault — to act before incidents occur.

What problem does VCV solve?#

Organizations using HashiCorp Vault often have to manage:

  • Multiple Vault instances and environments
  • Multiple PKI mounts (root, intermediate, teams/applications dedicated)
  • Certificates that expire at different rates, in different places
  • The need for a simple and unique interface

VCV focuses on the essentials: visibility and clarity.

What you get#

A clear certificate inventory#

VCV lists the certificates issued by your Vault PKI engines with the useful information:

  • Common Name (CN)
  • Issuer / source mount
  • Expiration date (the most important)
  • Days remaining
  • Status (revoked, expired, valid)

The goal: quickly answer questions like:

  • “Which certificates are expiring soon?”
  • “Which PKI mount produces the most short-lived certificates?”
  • “Are we safe for the next X days?”

Configurable expiration thresholds#

Policies vary depending on environments. VCV offers configurable expiration thresholds (for example: warning at 30 days, critical at 7 days) to match your operational practices without modifying the code.

A simple web interface#

VCV is designed to be:

  • Easy to run (self-hosted)
  • Fast to use
  • Accessible to operators

No unnecessary complexity: just the dashboard.

Ready for monitoring (metrics & alerting)#

VCV exposes metrics to integrate with your monitoring stack (e.g. Prometheus or VictoriaMetrics). You can then create alerts such as:

  • “Certificates expiring in < 7 days”
  • “Vault connectivity issue”
  • “Last retrieval too old”

How VCV integrates into your workflow#

VCV does not seek to replace Vault or your monitoring. It complements the ecosystem:

  • Vault remains the source of truth
  • VCV makes the data easy to consume
  • Prometheus/Alertmanager (or equivalent) triggers alerts at the right time

Concretely, teams use VCV as:

  • A daily/weekly verification dashboard
  • A prevention tool for incidents
  • A quick view during PKI Vault diagnostics

For whom?#

VCV is particularly useful if you:

  • Exploit Vault PKI in one or multiple instances
  • Manage multiple PKI mounts
  • Support many internal services with short-lived certificates
  • Look for a lightweight UI to spot risks early

Why VCV is deliberately “small”#

Many certificate management platforms become full-fledged ecosystems. VCV remains deliberately focused:

  • Minimal operational complexity
  • Simple deployment and updates
  • Clear UI and immediate value

If you already trust Vault for PKI, VCV helps you trust your visibility.

Application administration#

VCV is designed to remain easy to operate: administrators configure the connection to Vault (addresses and authentication), select the PKI mounts to expose, and adjust expiration thresholds according to the organization’s policy.

Via a password-protected page, the administration area will present forms to add all the paths to the Vault instances you manage. All information is stored in a JSON format file.

VCV does not replace Vault governance: access control and secret management remain handled by Vault; the admin’s challenge is secure configuration, observability, and regular credential rotation.

Deployment with docker#

Prerequisites#

The application configuration information is stored in a settings.json file. You must create this file before launching the container.

Create this settings.json file in the working directory, and enter these details by replacing the values with your own data:

{
  "app": {
    "env": "prod",
    "logging": {
      "level": "info",
      "format": "json",
      "output": "both",
      "file_path": "/var/log/app/vcv.log"
    },
    "port": 52000
  },
  "certificates": {
    "expiration_thresholds": {
      "critical": 2,
      "warning": 10
    }
  },
  "cors": {
    "allowed_origins": ["http://localhost:4321", "http://localhost:3000"],
    "allow_credentials": true
  },
  "vaults": [
    {
      "id": "vault-main",
      "address": "http://vault:8200",
      "token": "root",
      "pki_mounts": ["pki", "pki_dev", "pki_stage", "pki_production"],
      "display_name": "Vault",
      "tls_ca_cert_base64": "BASE64_PEM_CA_BUNDLE",
      "tls_ca_cert": "",
      "tls_ca_path": "",
      "tls_server_name": "vault.service.consul",
      "tls_insecure": true,
      "enabled": true
    },
    {
      "id": "vault-dev",
      "address": "http://vault-dev:8200",
      "token": "root",
      "pki_mounts": ["pki", "pki_corporate", "pki_external", "pki_partners"],
      "display_name": "Vault dev",
      "tls_ca_cert_base64": "BASE64_PEM_CA_BUNDLE",
      "tls_ca_cert": "",
      "tls_ca_path": "",
      "tls_server_name": "vault-dev.service.consul",
      "tls_insecure": true,
      "enabled": true
    }
  ]
}

Quick test with docker run#

Simply enter this command to launch vcv and access VCV:

docker run -d \
  -v "$(pwd)/settings.json:/app/settings.json:rw" \
  -v "$(pwd)/logs:/var/log/app:rw" \
  --cap-drop=ALL --read-only --security-opt no-new-privileges:true \
  -p 52000:52000 jhmmt/vcv:1.6

Production deployment with docker-compose#

Create this docker-compose.yml file and enter these details:

---
services:
  vcv:
    image: jhmmt/vcv:1.6
    container_name: vcv
    restart: unless-stopped
    ports:
      - "52000:52000/tcp"
    cap_drop:
      - ALL
    read_only: true
    security_opt:
      - no-new-privileges:true
    environment: # admin password: 'admin'
      - VCV_ADMIN_PASSWORD=$$2y$$10$$.SWJT4Amz9tyq4qq2vqQyOZm/KZAf38YVVNxj/6EvYLk4I0C6Q9A2
      - SETTINGS_PATH=/app/settings.json
    volumes:
      - ./settings.json:/app/settings.json:rw
    deploy:
      resources:
        limits:
          cpus: "0.50"
          memory: 64M

Deployment in Kubernetes#

Since VaultCertsViewer is a single-binary image, it is entirely possible to deploy the application in a Kubernetes cluster. Here is the complete manifest to use:

apiVersion: v1
kind: Namespace
metadata:
  name: vcv
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: vcv-sa
  namespace: vcv
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vcv
  namespace: vcv
  labels:
    app: vcv
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vcv
  template:
    metadata:
      labels:
        app: vcv
    spec:
      serviceAccountName: vcv-sa
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
        - name: vcv
          image: jhmmt/vcv:1.6
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 52000
              protocol: TCP
          volumeMounts:
            - name: vcv-settings
              mountPath: /app/settings.json
              subPath: settings.json
              readOnly: true
          resources:
            requests:
              cpu: "100m"
              memory: "64Mi"
            limits:
              cpu: "500m"
              memory: "128Mi"
          readinessProbe:
            httpGet:
              path: /api/ready
              port: http
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /api/health
              port: http
            initialDelaySeconds: 10
            periodSeconds: 20
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
      volumes:
        - name: vcv-settings
          secret:
            secretName: vcv-settings
---
apiVersion: v1
kind: Secret
metadata:
  name: vcv-settings
  namespace: vcv
type: Opaque
stringData:
  settings.json: |
    {
      "app": {
        "env": "prod",
        "port": 52000,
        "logging": {
          "level": "info",
          "format": "json",
          "output": "stdout",
          "file_path": "/var/log/app/vcv.log"
        }
      },
      "cors": {
        "allowed_origins": [],
        "allow_credentials": true
      },
      "certificates": {
        "expiration_thresholds": {
          "critical": 7,
          "warning": 30
        }
      },
      "vaults": [
        {
          "id": "vault-main",
          "address": "https://vault-prod.example.com:8200",
          "token": "change-me",
          "pki_mounts": [
            "pki",
            "pki_dev",
            "pki_stage",
            "pki_production"
          ],
          "display_name": "Vault",
          "tls_ca_cert_base64": "BASE64_PEM_CA_BUNDLE",
          "tls_ca_cert": "",
          "tls_ca_path": "",
          "tls_server_name": "vault.service.consul",
          "tls_insecure": false,
          "enabled": true
        },
        {
          "id": "vault-dev",
          "address": "https://vault-dev.example.com:8200",
          "token": "change-me",
          "pki_mounts": [
            "pki",
            "pki_corporate",
            "pki_external",
            "pki_partners"
          ],
          "display_name": "Vault dev",
          "tls_ca_cert_base64": "BASE64_PEM_CA_BUNDLE",
          "tls_ca_cert": "",
          "tls_ca_path": "",
          "tls_server_name": "vault-dev.service.consul",
          "tls_insecure": false,
          "enabled": true
        }
      ]
    }
---
apiVersion: v1
kind: Service
metadata:
  name: vcv
  namespace: vcv
  labels:
    app: vcv
spec:
  selector:
    app: vcv
  ports:
    - name: http
      port: 52000
      targetPort: http
      protocol: TCP
  type: ClusterIP

After this installation, you will need to use your Gateway and create the necessary HTTPRoute to access the app from outside the Kubernetes cluster.

Conclusion#

Certificate expiration is a predictable risk — and yet it remains very common.

VCV facilitates tracking, prioritization, and proactive action by providing a simple view of certificates across multiple Vault PKI engines, with configurable thresholds and metrics ready for monitoring.

6 minutes
1075 words