Snow-White is distributed as a Helm chart and can be deployed to any Kubernetes cluster.

Installation

Snow-White is distributed as a Helm chart. If you haven’t used Helm before, refer to the official documentation to get started.

Once Helm has been set up correctly, add the repository:

helm repo add snow-white https://bbortt.github.io/snow-white
helm repo update
helm search repo snow-white

Then, continue by installing the Chart:

helm install my-snow-white snow-white/snow-white \
  --set snowWhite.host=[PUBLIC_HOST]

Replace [PUBLIC_HOST] with the domain Snow-White will be reachable on.

You must also enable one of the following ingress options:

Option Value Notes
Kubernetes Gateway API (recommended) snowWhite.httproute.enabled=true  
Kubernetes Ingress snowWhite.ingress.enabled=true NGINX Ingress Controller is retired

Uninstall

helm uninstall my-snow-white

Persistent volume claims are not automatically cleaned up:

kubectl delete pvc --all

API Indexation

By default, Snow-White does not deploy an API synchronization job.

For a quick start, register specifications manually via curl. The api-index-api OpenAPI spec describes how to register a specification.

For production environments, enable the bundled CronJob:

snowWhite.apiSyncJob.enabled=true

Currently, JFrog Artifactory is the only supported API source. Snow-White expects a generic Artifactory repository containing specs as plain text files.

snowWhite.apiSyncJob.artifactory.baseUrl: 'http://localhost:8082/artifactory'
snowWhite.apiSyncJob.artifactory.repository: 'snow-white-generica-local'

Snow-White stores references to spec URLs, not the specs themselves. Artifactory must remain available at all times.

Artifactory Access Token

Inject the token via a Kubernetes Secret:

snowWhite:
  apiSyncJob:
    additionalEnvs:
      - name: SNOW_WHITE_API_SYNC_JOB_ARTIFACTORY_ACCESS_TOKEN
        valueFrom:
          secretKeyRef:
            name: artifactory-secret
            key: artifactory-token

Memory Management

The sync job ships with 1024Mi memory and 500m CPU and processes 3 specs in parallel.

Increase memory if you hit OOM with large specs:

snowWhite:
  apiSyncJob:
    resources:
      memory:
        request: 2048Mi
        limit: 2048Mi

Reduce parallelism to lower peak memory at the cost of sync time:

snowWhite:
  apiSyncJob:
    additionalEnvs:
      - name: SNOW_WHITE_API_SYNC_JOB_MAX_PARALLEL_SYNC_TASKS
        value: 1

Ingesting OTeL Data

Snow-White deploys its own OTel Collector by default. That collector is used both for ingesting tracing data, as well as exposing Snow-White’s own metrics.

Ingesting tracing data is a core concept of Snow-White (see “How It Works”). Snow-White cannot function without OTeL data.

At the same time, Snow-White is a telemetry sink, not a monitoring backend. It only persists trace data — all other signal types (logs, metrics) are dropped. You only need to connect to the /v1/traces endpoint; sending other signals has no effect.

The bundled OTel Collector and its storage are dedicated to Snow-White’s own use. Because Snow-White also drops attributes and telemetry it does not need, the data it holds is unsuitable for general service monitoring.

The recommended approach is to deploy your own OTel Collector in front of Snow-White and use it to fan out data: route a copy to your monitoring backend and a copy to Snow-White.

On the other hand, if your infrastructure already includes an InfluxDB cluster, you might as well use that instead (see “Disable InfluxDB”)

In-Cluster

To connect services running inside the same Kubernetes cluster, target the OTel Collector service directly via its FQDN:

http://<release-name>-snow-white-otel-collector.<namespace>.svc.cluster.local:4317

For example, with the Helm release name my-snow-white deployed to the observability namespace:

http://snow-white-otel-collector-my-snow-white.observability.svc.cluster.local:4317

Outside the Cluster

The OTel Collector is also reachable through the public ingress by default, via the /v1/traces path. For example, if Snow-White is available at https://my.snow.white, send traces to:

https://my.snow.white/v1/traces

This is controlled by the otelCollector.exposeThroughApiGateway Helm value, which defaults to true. Set it to false to restrict OTel ingestion to in-cluster access only:

otelCollector:
  exposeThroughApiGateway: false

Exporting Telemetry

Snow-White exposes its own OTEL telemetry. Connect it to an external collector:

otelCollector:
  connectToExternalOtelCollector:
    endpoint: 'my-endpoint:4317'
    exportLogs: true
    exportMetrics: true
    exportTraces: true

Monitoring the OTel Collector

The bundled collector exposes Prometheus metrics on port 8888 at /metrics. Enable scraping with:

otelCollector:
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/port: '8888'
    prometheus.io/path: '/metrics'

Replacing Bundled Infrastructure

Disable PostgreSQL

Add this to your values.yaml:

postgresql:
  enabled: false

When disabled, configure datasource env vars for api-index-api, quality-gate-api, and report-coordinator-api. Example with credentials from a Secret:

postgresql:
  enabled: false

snowWhite:
  apiIndexApi:
    additionalEnvs:
      - name: SPRING_DATASOURCE_URL
        value: jdbc:postgresql://my.database:5432/api-index-api
      - name: SPRING_DATASOURCE_USERNAME
        value: api-index-api
      - name: SPRING_DATASOURCE_PASSWORD
        valueFrom:
          secretKeyRef:
            name: my-postgresql-credentials
            key: api-index-password
  qualityGateApi:
    additionalEnvs:
      - name: SPRING_DATASOURCE_URL
        value: jdbc:postgresql://my.database:5432/quality-gate-api
      - name: SPRING_DATASOURCE_USERNAME
        value: quality-gate-api
      - name: SPRING_DATASOURCE_PASSWORD
        valueFrom:
          secretKeyRef:
            name: my-postgresql-credentials
            key: quality-gate-api-password
  reportCoordinatorApi:
    additionalEnvs:
      - name: SPRING_DATASOURCE_URL
        value: jdbc:postgresql://my.database:5432/report-coordinator-api
      - name: SPRING_DATASOURCE_USERNAME
        value: report-coordinator-api
      - name: SPRING_DATASOURCE_PASSWORD
        valueFrom:
          secretKeyRef:
            name: my-postgresql-credentials
            key: report-coordinator-api-password

Snow-White won’t accept a deployment that is missing any of the above environment variables.

For security, use DML-only credentials at runtime and separate DDL credentials for Flyway via SPRING_FLYWAY_USER / SPRING_FLYWAY_PASSWORD.

Disable InfluxDB

To disable the bundled InfluxDB StatefulSet, add the following to your values.yaml:

influxdb2:
  enabled: false

When InfluxDB is disabled, it is also recommended to disable OTel data ingestion. Without a configured backend, the OTel collector has nowhere to persist the collected data and will continuously log ingestion errors.

otelCollector:
  disableIngestion: true

When OTel ingestion is disabled, configure the datasource environment variables for the openapi-coverage-stream. For example, the following configuration uses a token stored in a Kubernetes Secret:

snowWhite:
  openapiCoverageStream:
    tempo:
      endpoint: ''
      tokenFromSecret:
        name: my-grafana-tempo-credentials
        key: grafana-tempo-token

Grafana Tempo supports either username/password or token-based authentication.

InfluxDB Static Credentials (GitOps)

When using Argo CD or similar GitOps operators, regenerated credentials will break connectivity on each sync. Provide static credentials via a Secret:

apiVersion: v1
kind: Secret
metadata:
  name: custom-influxdb-credentials
data:
  admin-password: YWRtaW4tcGFzc3dvcmQ=
  admin-token: YWRtaW4tdG9rZW4=
influxdb2:
  adminUser:
    existingSecret: custom-influxdb-credentials

Custom Truststores

Truststores must be in JKS format, stored in a Kubernetes Secret:

jssecacerts:
  secretName: 'my-secret'
  key: 'truststore.jks'

The JRE picks up the truststore automatically from $JAVA_HOME/lib/security/jssecacerts.