Skip to content

Kubernetes Deployment (Helm)

Audience: Administrators deploying Sentari on Kubernetes

OpenShift users: See OpenShift Deployment instead.


Sentari includes a production-grade Helm chart that deploys the API server, Celery workers, Celery Beat scheduler, TimescaleDB, and Redis.


Prerequisites

  • Helm 3.14+ installed
  • kubectl configured for your cluster
  • A namespace created for Sentari
  • Persistent storage available in the cluster (standard StorageClass)

Step 1 — Create a namespace

kubectl create namespace sentari

Step 2 — Choose a deployment mode

Option A: Bundled database (evaluation / smaller deployments)

Deploys TimescaleDB and Redis as StatefulSets alongside the application. Equivalent to Docker Compose but on Kubernetes.

helm install sentari deploy/helm/sentari/ \
  --namespace sentari \
  --set secrets.secretKey="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" \
  --set secrets.dbPassword="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
  --set secrets.redisPassword="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
  --set secrets.enrollmentToken="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
  --set secrets.bootstrapAdminEmail="admin@yourorganisation.com" \
  --set secrets.bootstrapAdminPassword="ChangeMe123!"

Use this when you have an existing managed TimescaleDB / PostgreSQL and Redis service.

helm install sentari deploy/helm/sentari/ \
  --namespace sentari \
  --set database.external=true \
  --set redis.external=true \
  --set secrets.databaseUrl="postgresql+psycopg2://sentari:<password>@<host>:5432/sentari" \
  --set secrets.redisUrl="redis://:<password>@<host>:6379/0" \
  --set secrets.secretKey="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" \
  --set secrets.enrollmentToken="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
  --set secrets.bootstrapAdminEmail="admin@yourorganisation.com" \
  --set secrets.bootstrapAdminPassword="ChangeMe123!"

The chart creates the Kubernetes Secret for you from these values — no separate step required.

Enterprise secrets management (Vault, Sealed Secrets, Argo CD)

If your organisation requires secrets to never appear in Helm's release history (Helm stores all --set values in the cluster and helm get values can expose them), pre-create the Secret outside Helm and reference it by name:

# Create the Secret independently (e.g. via Vault, Sealed Secrets, or kubectl)
kubectl create secret generic sentari-credentials \
  --namespace sentari \
  --from-literal=database-url="postgresql+psycopg2://sentari:<password>@<host>:5432/sentari" \
  --from-literal=redis-url="redis://:<password>@<host>:6379/0" \
  --from-literal=secret-key="<64-hex-char JWT key>" \
  --from-literal=enrollment-token="<enrollment token>" \
  --from-literal=bootstrap-admin-email="admin@yourorganisation.com" \
  --from-literal=bootstrap-admin-password="<password>"

# Install — no secrets in Helm values
helm install sentari deploy/helm/sentari/ \
  --namespace sentari \
  --set database.external=true \
  --set redis.external=true \
  --set secrets.existingSecretName=sentari-credentials

Install directly from the published Helm chart on the GitHub Container Registry. This avoids maintaining a local copy of the chart directory and always pulls the specified version.

Authenticate first:

# Authenticate to the Sentari OCI registry with your pull token
echo "PULL_TOKEN" | helm registry login ghcr.io -u sentari-customer --password-stdin

Install:

helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version 1.0.0 \
  -f values.yaml \
  --namespace sentari \
  --create-namespace

Where values.yaml contains the secrets and configuration for your environment (see Option A or B above for the required values). Keeping configuration in a values file rather than --set flags avoids secrets appearing in shell history.

Upgrade to a new version:

helm upgrade sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version <new-version> \
  -f values.yaml \
  --namespace sentari

Option D: Air-gapped / enterprise registry

If your cluster cannot reach the internet or requires images from an internal registry (Nexus, Artifactory, Harbor):

helm install sentari deploy/helm/sentari/ \
  --namespace sentari \
  --set image.registry=registry.corp.internal \
  --set database.image.registry=registry.corp.internal \
  --set redis.image.registry=registry.corp.internal \
  ... (other values as above)

Mirror these images to your internal registry before deploying:

  • ghcr.io/sentari-dev/sentari/server:<version>
  • ghcr.io/sentari-dev/sentari/docs:<version>
  • docker.io/timescale/timescaledb:2.14.2-pg16
  • docker.io/redis:7.2-alpine

Step 3 — Verify the deployment

# Watch pods come up
kubectl get pods -n sentari -w

# Check the API health endpoint
kubectl port-forward svc/sentari-api 8000:8000 -n sentari &
curl http://localhost:8000/api/health
# Expected: {"status": "ok", "version": "..."}

License required for write operations: Without a valid license, the server boots in a degraded read-only mode — reads work, but writes return HTTP 402. Set secrets.licenseKey (or include license-key in your pre-created Secret) to activate full functionality.


Proxy configuration

If your cluster routes outbound traffic through a corporate proxy (non-transparent), configure the proxy section in your Helm values. This is required for Sentari to reach external CVE feeds, SIEM webhooks, or the license server.

When to configure

Set these values when pods cannot reach the internet directly — for example, in locked-down enterprise clusters with egress filtering.

Helm values

proxy:
  httpProxy: "http://proxy.corp.internal:3128"
  httpsProxy: "http://proxy.corp.internal:3128"
  noProxy: "localhost,127.0.0.1,.cluster.local"

Or via --set flags:

helm install sentari deploy/helm/sentari/ \
  --namespace sentari \
  --set proxy.httpProxy="http://proxy.corp.internal:3128" \
  --set proxy.httpsProxy="http://proxy.corp.internal:3128" \
  ... (other values)

The chart automatically appends release-prefixed service names (e.g. sentari-db, sentari-redis) to NO_PROXY so that internal traffic is never sent through the proxy.

If your proxy requires authentication, include credentials in the URL:

proxy:
  httpsProxy: "http://username:password@proxy.corp.internal:3128"

Verifying proxy connectivity

After deploying, log in to the dashboard, navigate to Settings, and trigger a CVE sync. Check the worker logs for confirmation:

kubectl logs -n sentari deployment/sentari-worker -f

Key Helm values reference

Value Default Description
secrets.existingSecretName "" Use a pre-created K8s Secret instead of chart values
secrets.dbPassword "" PostgreSQL password (bundled DB mode)
secrets.redisPassword "" Redis password (bundled Redis mode)
secrets.secretKey "" JWT signing key (always required)
secrets.licenseKey "" License key — without it writes return HTTP 402
database.external false Use external DB instead of bundled TimescaleDB
redis.external false Use external Redis instead of bundled
image.registry ghcr.io Registry for the server image (repository defaults to sentari-dev/sentari/server)
database.image.registry docker.io Registry for the TimescaleDB image
redis.image.registry docker.io Registry for the Redis image
api.replicas 1 Number of API pod replicas
worker.replicas 1 Number of Celery worker replicas
proxy.httpProxy "" HTTP proxy URL for outbound connections
proxy.httpsProxy "" HTTPS proxy URL for outbound connections
proxy.noProxy "localhost,127.0.0.1,.cluster.local" Hosts to bypass the proxy

Upgrading

helm upgrade sentari deploy/helm/sentari/ \
  --namespace sentari \
  --reuse-values \
  --set image.tag=<new-version>

Database migrations run automatically on startup. Downgrading is not supported — always back up the database before upgrading.


Troubleshooting

Pods not starting

kubectl describe pod -n sentari <pod-name>
kubectl logs -n sentari <pod-name>

API does not become healthy

Check if database migrations succeeded:

kubectl logs -n sentari deployment/sentari-api | grep -i "migration\|alembic\|error"

Agent cannot connect to server

  1. Verify the server URL is reachable from the endpoint: curl https://<server>:8000/api/health
  2. Check the agent log: /var/log/sentari/agent.log
  3. If certificates are corrupted, re-enroll: delete /var/lib/sentari/certs/ and run with --enroll-token

No scans appearing in dashboard

  1. Confirm the agent service is running: systemctl status sentari-agent
  2. Check the agent log for upload errors
  3. Verify the enrollment token matches between agent config and server

For additional assistance, contact your Sentari representative or visit the support portal.