Skip to content

Deploy on Kubernetes (Helm)

charts/pantahub in the pantahub-base repository is a Helm translation of the repo's docker-compose.yml: the same services, same names and ports, running on a Kubernetes cluster instead of one Docker host. It targets dev/test clusters (k3d, minikube, kind, or a shared dev cluster); all images are public, so no pull secrets are needed.

The chart ships its own detailed docs — charts/pantahub/GUIDE.md (step-by-step) and charts/pantahub/README.md (compose-to-chart mapping and caveats). This page is the short version.

Prerequisites

  • kubectl and Helm v3.

  • A cluster with ~6 GB of RAM to spare and a default StorageClass (the chart creates 7 PVCs). For example:

    sh
    k3d cluster create pantahub --agents 1     # ships local-path storage
    # or: minikube start --memory 8192 --cpus 4
    # or: kind create cluster --name pantahub

Configure

Your values file plays the role of .env.local: everything under env: is merged over the defaults in values.yaml (which mirror env.default, including the dev-only JWT/JWE keys — replace those for any cluster other people can reach).

yaml
# my-values.yaml
env:
  PANTAHUB_SA_ADMIN_SECRET: "changeme"

kibana:
  enabled: true          # log UI (the compose "logs" profile)

base:
  tag: develop           # any pantahub-base image tag

Mind the demo accounts: without PANTAHUB_PRODUCTION set, base runs in dev mode and enables all built-in accounts with their code defaults — including admin/admin. The PANTAHUB_DEMOACCOUNTS_PASSWORD_* variables only apply in production mode (PANTAHUB_PRODUCTION: "yes"), which disables any account without an explicit password. Set both before exposing the cluster.

Every other credential of the backing stack is also a value, defined in exactly one place and rendered wherever it's used (mongo user creation, kafka-connect connector configs, kibana.yml): the app mongo user (env.MONGO_USER/MONGO_PASS), the mongo root user (mongo.env.*), the kafka-connect mongo users (mongo.connectUsers.*), the replica-set keyfile (mongo.keyfileKeys) and kibana's encryption keys (kibana.encryptionKeys.*). Two gotchas: mongo users are created on first bring-up only (changing a password later doesn't update an existing data volume), and mongo passwords end up inside mongodb:// URIs — keep them URL-safe.

Install

sh
helm install pantahub ./charts/pantahub -n pantahub \
  --create-namespace -f my-values.yaml

kubectl -n pantahub get pods -w

First bring-up takes several minutes. Kubernetes has no depends_on, so ordering works the way the compose entrypoints already did — wait loops: mongo initializes rs0, kafka creates topics, kafka-connect uploads the connector configs, phs waits for them, and base has an initContainer waiting on mongo, fluentd and elasticsearch. Some early CrashLoopBackOff cycles on the kafka side are expected and self-heal.

Access the services

The chart keeps the compose ports; port-forward what you need:

sh
kubectl -n pantahub port-forward svc/base 12365:12365   # API
kubectl -n pantahub port-forward svc/www  3000:80       # Web UI
kubectl -n pantahub port-forward svc/pvr  12367:12367   # pvr

The www defaults assume exactly these forwards, so with the three running the UI at http://localhost:3000 works out of the box. On a local cluster you can also enable the ingress without a domain — it falls back to the hosts api.localhost / hub.localhost / pvr.localhost.

Accessed this way the cluster is a development environment: fine for the web UI, the API and pvr, but real Pantavisor devices cannot connect to it — devices require a TLS endpoint with a certificate they trust, which localhost port-forwards and *.localhost hosts can't provide. You can still populate it with simulated devices using pantavisor-mocker (init --host localhost --port 12365 against the forwarded API). For real hardware, continue to the next section.

Going public: a domain with Let's Encrypt

To serve real clients — above all, real devices — the chart can expose the three client-facing services on subdomains of your own domain, with TLS certificates requested and renewed by cert-manager:

  • api.<domain> → base (devices and API clients)
  • hub.<domain> → www (the web UI)
  • pvr.<domain> → pvr

Once per cluster, install an ingress controller and cert-manager — on stock k3s you can skip the ingress controller, since its bundled traefik is the default IngressClass and the chart uses the cluster default when ingress.className is left empty. Point api., hub. and pvr.<domain> — or a wildcard — at the ingress controller's external IP (the node's public IP on single-node k3s). Let's Encrypt HTTP-01 validation needs the names to resolve publicly and port 80 reachable; with DNS in place before the install, issuance typically completes in under a minute. Then:

yaml
# my-values.yaml
ingress:
  enabled: true
  # className: nginx        # only when not using the cluster default
  domain: pantahub.example.com
  letsencrypt:
    email: you@example.com  # ACME account email

Setting ingress.domain rewires the whole app automaticallyPANTAHUB_HOST, PANTAHUB_SCHEME, PH_AUTH and the UI's REACT_APP_* URLs all switch to https://api./hub./pvr.<domain>, and base/gc/www roll to pick it up. The certificate for all three hosts lands in the pantahub-tls secret; watch progress with kubectl -n pantahub get certificate,order,challenge, and use the Let's Encrypt staging server (ingress.letsencrypt.server) while testing to avoid rate limits.

Two things to do before devices can use it:

  • Lock the cluster down before it is publicly reachable: set PANTAHUB_PRODUCTION plus explicit PANTAHUB_DEMOACCOUNTS_PASSWORD_* values (dev mode ships open demo accounts, admin/admin included), and replace all the well-known dev credentials — the JWT/JWE keys, mongo users and replica-set keyfile, kafka-connect users, kibana encryption keys. Each is a single value in my-values.yaml; see the credentials list in the Configure section and the chart README's "Dev secrets" caveat.
  • Build device images that talk to api.<domain> instead of api.pantahub.com — see Devices for your own Hub.

Day-2 operations

  • Config change — edit my-values.yaml, then helm upgrade pantahub ./charts/pantahub -n pantahub -f my-values.yaml. base and gc roll automatically when env: changes.
  • New image on a moving tag (e.g. develop) — kubectl -n pantahub rollout restart deploy/base.
  • Run a cron endpoint nowkubectl -n pantahub create job --from=cronjob/pantahub-publicdevices manual-run-1.

Smoke test

charts/pantahub/smoke-test.sh validates the whole cycle unattended: it creates a throwaway k3d cluster (with its own kubeconfig — yours is not touched), installs the chart, waits for every deployment, probes the services from inside the cluster, checks the mongo replica set, and tears the cluster down again. KEEP=true keeps the cluster to poke around; VALUES=overlay.yaml tests extra values.

The pantacor and Confluent images are amd64-only, so the full run needs an amd64 host. Every component has an enabled flag in values, so on arm64 you can still exercise the chart mechanics with the arm64-capable subset (mongo, elasticsearch, localstack) by disabling the rest — the chart's GUIDE ships a ready-made smoke-arm64.yaml for exactly that, plus a recipe for a full end-to-end run (including the Let's Encrypt flow) on a throwaway cloud VM.

Uninstall / reset

sh
helm uninstall pantahub -n pantahub

PVCs — and therefore all data — deliberately survive uninstall. For a full wipe, kubectl -n pantahub delete pvc --all (or delete the namespace).

Things to know

  • One release per namespace, and the service names (mongo, kafka, base, …) are fixed — entrypoints and connector configs reference them.
  • PVCs stuck Pending means no default StorageClass — set global.storageClass or install one.
  • Elasticsearch needs vm.max_map_count=262144; a privileged initContainer sets it (disable with elasticsearch.sysctlInit.enabled=false and set it on the node yourself).
  • The chart bakes in several Kubernetes-specific fixes found by running it — enableServiceLinks: false (Confluent images crash on injected *_PORT vars), base's baked env.default neutralized so ConfigMap env wins, headless mongo Services with publishNotReadyAddresses — the chart README documents them; don't undo them.
  • The compose nginx mutual-TLS front is not translated — bring your own ingress config if you need client-certificate forwarding (TLS onboarding) on Kubernetes.

The full troubleshooting table lives in charts/pantahub/GUIDE.md.