Berserk Docs

Self-Hosting Berserk

What a self-hosted Berserk cluster needs and where each part is configured, with links to the detailed pages

Berserk is self-hosted with its Helm chart, in your own Kubernetes cluster. You provide a PostgreSQL database and an S3-compatible object store; Berserk runs everything else in the cluster. This page lists what you configure and links to the page for each. For a step-by-step install up to a first query, see Creating a Berserk Cluster.

Architecture at a glance

Two services take traffic from outside the cluster: the gateway (people, the CLI, the UI) and ingest (telemetry). Everything else is reachable only inside the cluster.

You provide:

  • A Kubernetes cluster, with an ingress controller, DNS and TLS for the two public edges.
  • PostgreSQL 17 or 18, for metadata.
  • S3-compatible object storage (AWS S3, GCS, Cloudflare R2, MinIO, …), for segment data. It is the source of truth for all telemetry.
  • Optional: an OIDC identity provider for single sign-on, or an authenticating reverse proxy.

Berserk runs, from the Helm chart: the gateway, meta, query, ingest, nursery, janitor, ui and permissions services. Their ports: Services and Helm Values.

Secrets

Berserk reads every credential from a Kubernetes Secret. For each, either Helm creates and manages it (managed: true), or you create it (managed: false) and the chart references it by name.

With helm install or helm upgrade, use Helm-managed Secrets (managed: true). With Argo CD or other GitOps tooling, create the Secrets yourself (managed: false): the managed path reads whether a Secret exists, which Argo's apply model does not support. Secrets you create yourself can come from Vault, the External Secrets Operator or SOPS.

The chart's Secrets:

  • postgres-credentials and s3-credentials: the database connection string and the object-store keys. Always required, except the S3 keys on EKS, where IRSA supplies them. External by default.
  • berserk-master-key: the key every internal secret derives from (cookie key, service tokens, session signing). Helm-managed and generated by default, and kept across upgrades. Rotation is a three-step edit of the Secret.
  • auth-postgres-credentials: only for an auth stack on a separate database; otherwise the auth stack uses postgres-credentials.

Dependencies has the full table, a Helm-managed and an external example for each Secret, and a full install command.

User integration

Berserk has no default credentials. The gateway creates the first admin, runs sign-in, and passes the signed-in identity to the UI and the services. An account is a local account, which signs in with a password, or an SSO account, which signs in through your identity provider. The first admin is a local account, created one of two ways:

  • One-time setup link (default): on first boot with no users, the gateway prints a single-use /setup?token=… URL to its logs. Open it and create the admin.
  • Pre-provisioned admin: the first admin comes from a value or a Secret (bootstrap_admin_email and bootstrap_admin_password), for unattended installs.

Other people sign in in one of two ways:

  • Single sign-on: a cluster admin connects your identity provider (Okta, Entra ID, Google Workspace, Keycloak, …) on Settings → Single sign-on, then adds SSO members on Settings → Members or lets new sign-ins create accounts. The identity provider never signs in a local account, and a local admin can always sign in to repair single sign-on.
  • Trusted reverse proxy: an authenticating proxy in front of the gateway names the user, in an email header (for example X-USER-HEADER) or as SPIFFE on-behalf-of (on-behalf-of: spiffe://<trust-domain>/user/<name>; the gateway strips the configured prefix and appends an email domain). The gateway trusts the header on network grounds, so it must be reachable only through the proxy.

The trusted proxy creates accounts at their first sign-in by default. With gateway.config.autoProvisionUsers: false (directory mode), only existing accounts sign in. For single sign-on, the same choice is the New sign-ins create accounts setting.

Accounts and Sign-in covers the first admin, the Single sign-on settings and the trusted proxy's values; Guide: Enabling Single Sign-on connects an identity provider. Single sign-on needs no Helm values or Secrets (Dependencies → Single sign-on).

Networking and ingress

There are two public edges. The gateway serves HTTP (the web UI, the REST/JSON query API and SSE result streams) and native gRPC under /api/grpc (the bzrk CLI and the SDKs). Ingest serves the ingest APIs.

Gateway: the public edge

The gateway serves both kinds of traffic on one port:

  • HTTP: the web UI, the REST/JSON query API and SSE result streams. An HTTP/HTTPS ingress carries these.
  • Native gRPC under /api/grpc: the bzrk CLI and SDKs. It needs HTTP/2 to the gateway, which an HTTP/1.1 ingress cannot carry.

Route /api/grpc as gRPC over HTTP/2, and everything else as HTTP:

  • nginx: backend-protocol applies to a whole Ingress, so use two Ingresses on the same host: /api/grpc annotated nginx.ingress.kubernetes.io/backend-protocol: "GRPC", and / without it. The chart renders this split: enable gateway.ingress (HTTP) and gateway.grpcIngress (gRPC) on the same host.
  • Traefik: set traefik.ingress.kubernetes.io/service.serversscheme: h2c on the gateway Service (gateway.service.annotations). Traefik then speaks h2c to the gateway on every path, and one gateway.ingress carries HTTP, SSE and gRPC.

Put the gRPC backend-protocol only on the /api/grpc Ingress. On the HTTP edge (/) it breaks the UI, the REST/JSON API and SSE streams. Users who reach the gateway only through the browser or the REST API need no gRPC Ingress.

Turn off response buffering on the HTTP edge (for example nginx proxy-buffering), so SSE events reach the client as they are produced.

HTTP/1.1 and HTTP/2 on two ports. The gateway's port detects HTTP/1.1 or HTTP/2 (h2c) per connection. If a proxy in front of the gateway cannot use two backend protocols for one endpoint, enable a second listener with the same routes:

gateway:
  extraListener:
    enabled: true
    port: 9501

The chart binds the extra port, adds it to the Service and opens it in the NetworkPolicy. Point the proxy's HTTP/1.1 cluster at the main port and its HTTP/2 (h2c) cluster at the extra one. Both ports accept either protocol.

Set global.publicBaseUrl to the public hostname; the gateway builds the single sign-on redirect URI and the one-time setup link from it. Terminate TLS at your ingress, or with tls: on the gateway Ingress, and keep gateway.config.cookieSecure: true in production. The gateway values every install sets: Accounts and Sign-in → Values every install sets.

Ingest

Senders reach ingest on a host separate from the gateway: the OTLP API over gRPC on 4317 (recommended), which needs HTTP/2 end-to-end, and over HTTP on 4318. Ingest also serves the Loki Push API (on by default) and the Datadog API (off by default). Ingest Endpoints covers their Ingresses, the 16 MiB request body limit and Settings → Endpoints.

Network policies

Optional Kubernetes NetworkPolicies limit every service to its known peers; only the gateway and ingest are reachable from outside. Enable them with global.networkPolicy.enabled: true (it needs a CNI that enforces NetworkPolicies), then allow external egress (S3 on 443, PostgreSQL on 5432) and, for a collector outside the cluster, OTLP egress. See Network Policies.

Other configuration

  • Sizing: CPU, memory, disk and object storage, from daily ingest, retention and the days kept in cache: Hardware Sizing.
  • Storage: services use local disk for the query segment cache and scratch space. S3 is the source of truth, so losing a local disk means a cold cache, not data loss: Storage.
  • Observability: the per-pod Prometheus /metrics endpoint, and Berserk's own logs, metrics and traces over OTLP to any collector, Berserk included: Observability, Service Metrics.
  • Upgrades: the release notes list what changes in each release and what to do.

Next steps

On this page