Berserk Docs

Dependencies

External dependencies and deployment configurations for Berserk (and managing secrets)

Berserk requires PostgreSQL (v17+) for metadata storage and S3-compatible object storage for segment data. The gateway also needs a small set of auth secrets (a cookie signing key plus internal service tokens), which the chart generates automatically by default. Depending on the features you enable you may also need an ingest token, a separate auth database, and an OIDC client secret. (The Git-synced query library's credentials are configured at runtime in the admin UI, not as a chart-managed Secret.)

The table below lists every Kubernetes Secret the chart uses. For each one you choose whether the Helm chart manages it for you (managed: true) or you create it yourself with kubectl (managed: false) — the per-secret sections that follow show both paths. The default differs per secret, as noted in the last column.

SecretPurposeWhen neededHelm-managed
postgres-credentialsMetadata DB connection string (meta, ui)AlwaysYes — off by default
s3-credentialsObject-store access keys (query, ingest, janitor, nursery)Always, unless EKS/IRSAYes — off by default
gateway-secretsCookie signing key + internal service tokens (gateway, ui, admin-ui, permissions)AlwaysYes — on by default
auth-postgres-credentialsConnection string for a separate auth-stack databaseOnly with a dedicated auth DB (otherwise shares postgres-credentials)Yes — off by default
gateway-oidc-secretsOIDC client_secret from your identity provider (gateway, admin-ui)Only with OIDC SSONo (external only)
ingest-tokenDefault ingest authentication tokenOptional (ingest auth)Yes — on by default

If you run helm upgrade/install directly, you can use managed: true, otherwise, like deploying via Argo or similar, it is best to create the secrets externally.

The reason is that when installing the helm chart, it needs to read whether an existing secret exists or not, which for example is not supported by Argo.

PostgreSQL Credentials

The meta service needs a PostgreSQL (v18+) connection string.

values.yaml
global:
  postgresCredentials:
    managed: true
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml \
  --set global.postgresCredentials.databaseUrl="postgres://<user>:<password>@host:5432/berserk"

Create the secret before installing:

kubectl create namespace bzrk

kubectl create secret generic postgres-credentials \
  --namespace bzrk \
  --from-literal=database_url="postgres://<user>:<password>@host:5432/berserk"
values.yaml
global:
  postgresCredentials:
    managed: false # default
    secretName: "postgres-credentials"
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml

S3 Object Store

Query, ingest, janitor, and nursery services all need access to an S3-compatible object store (AWS S3, MinIO, R2, etc.).

All approaches require the storage location in your values file:

values.yaml
global:
  storage:
    endpoint: "https://s3.eu-central-1.amazonaws.com"
    bucket: "your-berserk-bucket"
    region: "eu-central-1"
values.yaml
global:
  s3Credentials:
    managed: true
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml \
  --set global.s3Credentials.accessKeyId="AKIA..." \
  --set global.s3Credentials.secretAccessKey="secret..."

Create the secret before installing:

kubectl create namespace bzrk

kubectl create secret generic s3-credentials \
  --namespace bzrk \
  --from-literal=AWS_ACCESS_KEY_ID=<key> \
  --from-literal=AWS_SECRET_ACCESS_KEY=<secret>
values.yaml
global:
  s3Credentials:
    managed: false # default
    secretName: "s3-credentials"
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml

On EKS, S3 credentials are handled automatically through IAM Roles for Service Accounts (IRSA). No S3 secret is needed — Berserk picks up access from the pod's IAM role.

IAM Role Setup — create an IAM role with S3 permissions and associate it with a Kubernetes service account. If you use Terraform:

module "irsa" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  role_name = "berserk-s3-access"

  oidc_providers = {
    main = {
      provider_arn = module.eks.oidc_provider_arn
      namespace_service_accounts = ["bzrk:berserk"]
    }
  }
}

The IAM policy needs at minimum s3:GetObject, s3:PutObject, s3:ListBucket, and s3:DeleteObject on your data bucket.

values.yaml
global:
  s3Credentials:
    managed: false
  serviceAccountName: "berserk"

The serviceAccountName is applied to all Berserk pod specs, which lets the AWS SDK resolve credentials from the IRSA-annotated service account automatically.

helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml

Gateway Auth Secrets

The gateway signs session cookies and authenticates its internal calls to the ui, admin-ui, and permissions services. These values live in one Secret (gateway-secrets) with three keys:

  • cookie_signing_key — signs session cookies (must be at least 64 bytes)
  • service_token — presented on the X-Bzrk-Service-Token header so ui/admin-ui trust the identity the gateway injects
  • permissions_service_token — authenticates calls to the permissions service

Unlike the other secrets, this one is Helm-managed by default: the chart generates strong random values on first install and preserves them across upgrades, so most installs need no action here. Create it yourself only when integrating with an external secret manager, or when the values must survive a full uninstall/reinstall.

No action needed — the chart creates gateway-secrets with random values:

values.yaml
gateway:
  secrets:
    managed: true # default

To pin your own values instead of random ones, set cookieSigningKey, serviceToken, and permissionsServiceToken under gateway.secrets.

Create the secret before installing:

kubectl create namespace bzrk

kubectl create secret generic gateway-secrets \
  --namespace bzrk \
  --from-literal=cookie_signing_key="$(openssl rand -hex 64)" \
  --from-literal=service_token="$(openssl rand -hex 32)" \
  --from-literal=permissions_service_token="$(openssl rand -hex 32)"
values.yaml
gateway:
  secrets:
    managed: false
    secretName: "gateway-secrets"

ui, admin-ui, and permissions reference the same gateway-secrets Secret by default, so no further wiring is needed.

An optional fourth key, bootstrap_admin_password, seeds the first admin user at startup. See Admin UI Setup for the bootstrap-admin flow.

Auth Database (Optional)

The auth stack — gateway, admin-ui, permissions, and the optional source service — stores its tables in PostgreSQL. By default it shares the same database as meta (postgres-credentials) and isolates itself in an auth schema, so no extra secret is required.

To run the auth stack against a physically separate database, point it at its own connection string and opt out of the shared schema by setting global.authDatabaseSchema: "".

values.yaml
global:
  authPostgresCredentials:
    managed: true
    secretName: "auth-postgres-credentials"
  authDatabaseSchema: "" # opt out of the shared `auth` schema
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml \
  --set global.authPostgresCredentials.databaseUrl="postgres://<user>:<password>@host:5432/berserk_auth"

Create the secret before installing:

kubectl create namespace bzrk

kubectl create secret generic auth-postgres-credentials \
  --namespace bzrk \
  --from-literal=database_url="postgres://<user>:<password>@host:5432/berserk_auth"
values.yaml
global:
  authPostgresCredentials:
    managed: false # default
    secretName: "auth-postgres-credentials"
  authDatabaseSchema: ""

OIDC Client Secret (Optional)

When you enable OIDC single sign-on, the gateway needs the client_secret issued by your identity provider. Because that value comes from the IdP, this secret is always external — the chart never generates it. gateway and admin-ui read the same Secret and key.

kubectl create namespace bzrk

# client_secret must be at least 32 characters
kubectl create secret generic gateway-oidc-secrets \
  --namespace bzrk \
  --from-literal=client_secret="<client-secret-from-your-idp>"
values.yaml
gateway:
  config:
    oidc:
      enabled: true
      issuer: "https://idp.example.com"
      clientId: "berserk"
  secrets:
    oidcClientSecret:
      existingSecret:
        name: "gateway-oidc-secrets"
        key: "client_secret"

admin-ui:
  config:
    oidc:
      enabled: true
  secrets:
    oidcClientSecret:
      existingSecret:
        name: "gateway-oidc-secrets"
        key: "client_secret"

See Admin UI Setup for the full OIDC walkthrough.

Trusted-Proxy Authentication (Optional)

When you front the gateway with a trusted authenticating reverse proxy (PP), it can assert the signed-in user in a request header. Unlike the OIDC client secret, this path needs no secret material — identity is trusted on network grounds alone, so the gateway must be unreachable except through the proxy.

values.yaml
gateway:
  config:
    trustedProxy:
      enabled: true
      # Direct email header, SPIFFE on-behalf-of, or both:
      userHeader: X-PP-USER
      onBehalfOf:
        enabled: true
        header: on-behalf-of
        spiffePrefix: "spiffe://prod.example.net/user/"
        emailDomain: example.com

See Admin UI Setup → Path D for the full walkthrough, including directory mode and the security model.

Default Ingest Token

An ingest token authenticates data sent to the ingest service. By default, all requests into the ingest service are authenticated via the ingest token. If you want to allow unauthenticated requests to the ingest service, then set up a default ingest token as follows.

values.yaml
global:
  ingestToken:
    managed: true
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml

An init container on the ingest service waits for Meta to be ready, creates the token via Meta's API, and stores it in a K8s secret. On subsequent deployments the init container is skipped if the secret already exists.

Create the secret before installing:

kubectl create namespace bzrk

kubectl create secret generic ingest-token \
  --namespace bzrk \
  --from-literal=default_ingest_token="<token>"
values.yaml
global:
  ingestToken:
    managed: false # default
    secretName: "ingest-token"
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml

With this you can manage the ingest token secret directly.

UI Library (Optional)

The Query Library can be mirrored to a Git repo. This is not configured via Helm — a cluster admin sets the HTTPS remote and token at runtime in the admin UI, and it's stored in the database. See Git Sync Configuration for the walkthrough.

Full Install Example

When letting Helm manage all secrets, a single install command covers everything. The gateway's auth secrets (gateway-secrets) are generated automatically, so they need no flags below:

values.yaml
global:
  storage:
    endpoint: "https://s3.eu-central-1.amazonaws.com"
    bucket: "your-berserk-bucket"
    region: "eu-central-1"
  s3Credentials:
    managed: true
  postgresCredentials:
    managed: true
  ingestToken:
    managed: true
helm repo add berserk https://berserkdb.github.io/helm-charts
helm install berserk berserk/berserk \
  --namespace bzrk \
  -f values.yaml \
  --set global.s3Credentials.accessKeyId="AKIA..." \
  --set global.s3Credentials.secretAccessKey="secret..." \
  --set global.postgresCredentials.databaseUrl="postgres://user:password@host:5432/berserk" \

On subsequent helm upgrade calls, you do not need to pass the credential --set flags again — the secrets already exist in the cluster and the chart will not overwrite them.

See example values for minimal, Helm-managed, and EKS configurations.

On this page