Skip to content

hackertwinten/clair-helm

clair-helm

Lint and Test Release License Artifact Hub

Helm chart for deploying Clair — the open-source container vulnerability static analysis tool by Red Hat/Quay.

Supports two deployment modes:

  • combo (default) — single all-in-one Clair process running indexer + matcher, with an optional separate notifier pod
  • distributed — separate Deployment per component (indexer, matcher, notifier) with path-based Ingress routing

Contents


Prerequisites

  • Kubernetes 1.24+
  • Helm 3.10+
  • kubectl configured against your cluster
  • For distributed mode: a Layer 7 Ingress controller (nginx, traefik, etc.)

Installation

From the Helm repository (recommended)

helm repo add clair-helm https://hackertwinten.github.io/clair-helm
helm repo update
helm install clair clair-helm/clair -n clair --create-namespace

From source

git clone https://github.com/hackertwinten/clair-helm.git
cd clair-helm
helm install clair . -n clair --create-namespace

Verify the release:

helm status clair -n clair
kubectl get pods -n clair

Run the health check tests:

helm test clair -n clair

Port-forward to access the API locally (combo mode):

kubectl port-forward -n clair svc/clair 6060:6060
curl http://localhost:6060/api/v1/index_report

Deployment modes

Combo mode (default)

Runs all Clair components in a single process (CLAIR_MODE=combo). An optional second pod can handle notifications independently.

helm install clair ./clair-helm -n clair --create-namespace

Resources created:

Resource Description
Deployment/clair Clair main process in combo mode
Deployment/clair-notifier Dedicated notifier pod (combo.notifier.enabled=true)
Deployment/clair-postgresql Bundled PostgreSQL 15
Service/clair ClusterIP on port 6060 and 8089
Service/clair-notifier ClusterIP for the notifier
Service/clair-postgresql ClusterIP for Postgres
Secret/clair-db Database password (auto-generated)
Secret/clair-config Clair config YAML
PersistentVolumeClaim/clair-postgresql 10Gi data volume

Distributed mode

Runs each Clair component as its own Deployment. Requires a Layer 7 Ingress controller to route requests by path.

helm install clair ./clair-helm -n clair --create-namespace \
  --set mode=distributed \
  --set distributed.ingress.enabled=true \
  --set distributed.ingress.className=nginx \
  --set distributed.ingress.host=clair.example.com

Resources created:

Resource Description
Deployment/clair-indexer Indexer component
Deployment/clair-matcher Matcher component
Deployment/clair-notifier Notifier component (if enabled)
Service/clair-indexer ClusterIP for indexer
Service/clair-matcher ClusterIP for matcher
Service/clair-notifier ClusterIP for notifier
Ingress/clair Routes /indexer, /matcher, /notifier to each service
Deployment/clair-postgresql Bundled PostgreSQL 15
Secret/clair-config Shared Clair config YAML

The Ingress routes traffic by path prefix:

  • clair.example.com/indexer/* → indexer
  • clair.example.com/matcher/* → matcher
  • clair.example.com/notifier/* → notifier

Configuration

All options are in values.yaml. Common overrides:

Use an external PostgreSQL database

helm install clair ./clair-helm -n clair --create-namespace \
  --set postgresql.enabled=false \
  --set database.externalConnString="host=mydb.example.com port=5432 dbname=clair user=clair password=s3cr3t sslmode=require"

Set a specific database password

By default the chart generates a random 32-character password on first install and reuses it on upgrades. To set your own:

helm install clair ./clair-helm -n clair --create-namespace \
  --set database.password=mysecretpassword

Disable the notifier

# Combo mode
helm install clair ./clair-helm -n clair --create-namespace \
  --set combo.notifier.enabled=false

# Distributed mode
helm install clair ./clair-helm -n clair --create-namespace \
  --set mode=distributed \
  --set distributed.notifier.enabled=false

Enable Ingress (combo mode)

helm install clair ./clair-helm -n clair --create-namespace \
  --set combo.ingress.enabled=true \
  --set combo.ingress.className=nginx \
  --set "combo.ingress.hosts[0].host=clair.example.com" \
  --set "combo.ingress.hosts[0].paths[0].path=/" \
  --set "combo.ingress.hosts[0].paths[0].pathType=Prefix"

Enable HPA (combo mode)

helm install clair ./clair-helm -n clair --create-namespace \
  --set combo.autoscaling.enabled=true \
  --set combo.autoscaling.minReplicas=2 \
  --set combo.autoscaling.maxReplicas=5

Enable per-component HPA (distributed mode)

helm install clair ./clair-helm -n clair --create-namespace \
  --set mode=distributed \
  --set distributed.indexer.autoscaling.enabled=true \
  --set distributed.indexer.autoscaling.maxReplicas=10 \
  --set distributed.matcher.autoscaling.enabled=true \
  --set distributed.matcher.autoscaling.maxReplicas=10

Set resource limits

# custom-values.yaml
combo:
  resources:
    requests:
      cpu: 200m
      memory: 512Mi
    limits:
      cpu: 1000m
      memory: 1Gi

postgresql:
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 512Mi

Configure webhook notifications

# custom-values.yaml
config:
  notifier:
    webhook:
      target: "https://my-webhook.example.com/clair"
      callback: "http://clair:6060/notifier/api/v1/notifications"
      signed: false

Values reference

Global

Key Default Description
mode combo Deployment mode: combo or distributed
nameOverride "" Override the chart name
fullnameOverride "" Override the full release name
image.repository quay.io/projectquay/clair Clair image
image.tag 4.9.0 Image tag (defaults to appVersion)
config.logLevel info Log level: debug, info, warn, error
config.indexer.migrations true Run DB migrations on startup
config.matcher.period 6h How often to sync vulnerability data
config.matcher.disableUpdaters false Disable vulnerability feed updates

Database

Key Default Description
database.externalConnString "" Full PostgreSQL DSN (disables bundled Postgres)
database.name clair Database name
database.user clair Database user
database.password "" Password (auto-generated if empty)

PostgreSQL (bundled)

Key Default Description
postgresql.enabled true Deploy bundled PostgreSQL
postgresql.image.tag 17-alpine PostgreSQL image tag
postgresql.persistence.enabled true Enable PVC
postgresql.persistence.existingClaim "" Use a pre-existing PVC instead of creating one
postgresql.persistence.size 10Gi PVC size (ignored when existingClaim is set)
postgresql.persistence.storageClass "" Storage class (cluster default if empty)

Combo mode

Key Default Description
combo.replicaCount 1 Number of Clair replicas
combo.service.type ClusterIP Service type
combo.service.port 6060 HTTP API port
combo.ingress.enabled false Enable Ingress
combo.autoscaling.enabled false Enable HPA
combo.pdb.enabled false Enable PodDisruptionBudget
combo.notifier.enabled true Deploy a dedicated notifier pod
combo.notifier.replicaCount 1 Notifier replicas

Distributed mode

Key Default Description
distributed.ingress.enabled true Enable path-based Ingress (required for external access)
distributed.ingress.host clair.example.com Ingress hostname
distributed.ingress.className "" Ingress class name
distributed.indexer.replicaCount 2 Indexer replicas
distributed.indexer.autoscaling.enabled false Enable HPA for indexer
distributed.matcher.replicaCount 2 Matcher replicas
distributed.matcher.autoscaling.enabled false Enable HPA for matcher
distributed.notifier.enabled true Deploy notifier
distributed.notifier.replicaCount 1 Notifier replicas

Security

All pods run with:

  • runAsNonRoot: true
  • allowPrivilegeEscalation: false
  • readOnlyRootFilesystem: true
  • All Linux capabilities dropped

The Clair config (which contains the database password) is stored in a Kubernetes Secret, not a ConfigMap. Pods automatically restart when the config secret changes via a checksum annotation.


Upgrading

helm upgrade clair ./clair-helm -n clair

The database password is preserved across upgrades — the chart uses lookup to read the existing Secret and reuses the same password rather than generating a new one.


Uninstalling

helm uninstall clair -n clair

The PostgreSQL PVC is retained by default Kubernetes behavior. Delete it manually to free storage:

kubectl delete pvc clair-postgresql -n clair

About

Helm chart for Clair container vulnerability scanner

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors