Deploy the server with Helm
The chart is deliberately small: a Deployment, a Service, a ServiceAccount, and a Secret. It is the path for teams who already have a cluster and a managed Postgres.
For a single host, see Deploy the server with Docker Compose.
Before you start
Section titled “Before you start”- A registry credential for
ghcr.io/bugb-technologies/bugb-server. The image is published to a private repository, and the chart takes the credential as adocker-registrySecret throughimagePullSecrets. - A deployment key (
bugb_dk_live_…) or a pinned licence token. A published server holds a licence or answers402to everything. - The chart, which ships in the deployment bundle at
deploy/helm/. - A Postgres your platform team runs and backs up. RDS, Cloud SQL, or an operator-managed cluster. This is the supported arrangement.
- Kubernetes 1.23 or newer, and Helm 3.
Install
Section titled “Install”-
Give the cluster a way to pull the image.
image.repositoryalready points at the private package, so the credential is the only thing missing:Terminal window kubectl create namespace bugbkubectl -n bugb create secret docker-registry bugb-ghcr \--docker-server=ghcr.io \--docker-username="$BUGB_REGISTRY_USER" \--docker-password="$BUGB_REGISTRY_TOKEN"If the cluster cannot reach ghcr.io, mirror the image into a registry it can and override
image.repository. Setimage.digestrather than a tag if you want a reference that cannot be repointed at all; the publish workflow prints it. -
Put the DSN and the licence in Secrets you create yourself.
Terminal window kubectl -n bugb create secret generic bugb-db \--from-literal=BUGB_SERVER_DSN='postgresql://bugb:PASSWORD@postgres.db.svc.cluster.local:5432/bugb'kubectl -n bugb create secret generic bugb-license \--from-literal=BUGB_DEPLOYMENT_KEY='bugb_dk_live_xxxxxxxx'Create the licence Secret before the install, not after. A published server with no licence serves nothing, so a deployment installed without one comes up refusing every request.
Use
database.existingSecretrather than--set database.dsn=.... A DSN passed on the command line lands in your shell history and in the release’s stored values, wherehelm get valueshands it back to anyone with list rights.database.dsnexists for labs. -
Install.
Terminal window helm -n bugb install bugb ./deploy/helm \--set imagePullSecrets[0].name=bugb-ghcr \--set database.existingSecret=bugb-db \--set license.existingSecret=bugb-licenseThat renders a Deployment named
bugb-bugb-server, from the release name and the chart name, runningghcr.io/bugb-technologies/bugb-server:0.1.0.image.tagdefaults to the chart’sappVersion, so it is needed only when you want a different one. -
Watch it come up.
Terminal window kubectl -n bugb rollout status deploy/bugb-bugb-serverThe app applies its own schema on startup, so there is no separate migration job to run.
-
Check it reaches its database.
Terminal window kubectl -n bugb port-forward svc/bugb-bugb-server 8000:8000curl -s -o /dev/null -w '%{http_code}\n' localhost:8000/v1/repos401401is the good answer: the request reached the auth check, which opens a database connection before refusing you. A500here means the database is unreachable, and a402means the deployment holds no licence it accepts.
The licence Secret
Section titled “The licence Secret”The chart reads BUGB_DEPLOYMENT_KEY, BUGB_LICENSE_TOKEN and
BUGB_LICENSE_PUBLIC_KEY from the Secret named by license.existingSecret.
Every one of them is mounted optional: true, so a Secret carrying only one, or
a deployment part-way through a licence rollout, does not wedge the pod in
CreateContainerConfigError.
Set the deployment key or the pinned token, never both: a deployment
carrying both runs on the pinned value and ignores every token it fetches.
BUGB_LICENSE_PUBLIC_KEY is not read by a published image, which verifies
against the key stamped into it, so it is harmless to leave in the Secret and
the server logs one line saying it is ignoring it.
The inline equivalents are license.deploymentKey and license.token, which
are fine for a lab and put the credential in Helm’s release history. See
License a deployment for which values a
deployment should carry, and for the one combination that fails silently.
Mint tokens through the Deployment
Section titled “Mint tokens through the Deployment”kubectl -n bugb exec deploy/bugb-bugb-server -- \ python scripts/mint_token.py --tenant acme --identity alice --role memberminted member token for alice@acme — shown once, not storedbugb_ZRbsvgLqZdCncXcvGxvUOIEG3-KHjZMcy9i3YjdfUtUMinting is a write, so it is licence-gated: on a deployment holding no licence the script refuses rather than handing out a credential nothing will accept.
The first admin token is minted this way; after that a tenant’s own admin
manages their people over HTTP. There is no endpoint that mints across tenants
on purpose, because minting a token for any tenant is the ability to write into
any tenant, so it stays a DSN-in-hand operation.
What the chart does not do
Section titled “What the chart does not do”One replica, and a Recreate strategy. This is a correctness constraint, not
a default to tune. Event ingest assigns a sequence number by reading the current
maximum and continuing from it, serialised only by an in-process lock. Two pods,
including the two that briefly overlap during a rolling update, can interleave
sequence numbers within a run. A reordered log is a reordered fold, and that is a
wrong verdict arrived at silently.
Recreate trades a few seconds of downtime for that. The chart warns if you
raise replicaCount.
No Ingress template. Every cluster wants a different object, and pointing your own at the Service is a smaller job than fighting a template that guessed wrong. Bearer tokens travel over this Service, so do not expose it without TLS in front.
No Postgres subchart is declared. The supported database is one your platform
team already backs up. A Postgres that helm uninstall can take with it is the
wrong home for every finding your team has ever pushed. Chart.yaml carries a
commented dependencies: block and instructions if you want one anyway for a
proof of concept.
Hardened by default: non-root uid 10001, readOnlyRootFilesystem, all
capabilities dropped, and no service account token mounted, because the server
calls no Kubernetes API.
Upgrade
Section titled “Upgrade”helm -n bugb upgrade bugb ./deploy/helm --set image.tag=0.2.0The pod is replaced and the new one applies the schema on startup. helm rollback returns the image; it does not return the database, and migrations
are forward only. Take a database backup before an upgrade that changes the
schema, because that backup is the only real rollback.
Related
Section titled “Related”- License a deployment: the deployment key, and the Helm-specific trap when removing a pinned token.
- How licensing works: the term, the refresh, and what a lapse does.
- Connect the CLI to a server: the developer side.

