Skip to content

Deploy with Docker Compose

The docker-compose.yml at the root of pantahub-base brings up the whole cluster on one machine: the API built from your checkout (with hot reload), the web UI, and every backing service — mongo replica set, the kafka pipeline, elasticsearch/fluentd, localstack S3, gc and cron jobs.

Development environment

This setup is for developing and testing the platform itself — real Pantavisor devices cannot connect to it. Devices require a TLS endpoint with a certificate they trust, and the compose stack serves plain HTTP on localhost (its nginx front uses a self-signed localhost certificate). To serve real hardware, use the Helm chart with a public domain; to populate a dev Hub with devices, simulate them with pantavisor-mocker.

Prerequisites

  • Docker with the compose plugin, and roughly 8 GB of RAM to spare.

  • Elasticsearch needs the kernel setting vm.max_map_count=262144:

    sh
    sudo sysctl -w vm.max_map_count=262144
  • Two files/dirs the compose file expects to exist in the repo root:

    sh
    cd pantahub-base
    touch .env.local
    mkdir -p .data/elasticsearch

    .env.local is your local override of env.default (it can start empty); .data/ is where all persistent state lands (mongo, elasticsearch, kafka, localstack).

Configure

env.default documents every knob and ships working development defaults — including dev-only JWT/JWE keys that must be replaced for anything public. Put your overrides in .env.local; it is layered on top. A useful minimum:

sh
# .env.local

# basic-auth secret for the cron/admin endpoints (user: saadmin)
PANTAHUB_SA_ADMIN_SECRET=changeme

A note on the built-in demo accounts (admin, user1user3, …): unless PANTAHUB_PRODUCTION is set, base runs in dev mode and enables all of them with their code defaults — including admin/admin. The PANTAHUB_DEMOACCOUNTS_PASSWORD_* variables only take effect in production mode (PANTAHUB_PRODUCTION=yes), where accounts without an explicit password are disabled. For anything reachable by others, set both.

Bring it up

sh
docker compose up -d
docker compose ps

First bring-up takes a few minutes and is deliberately self-ordering: mongo initializes replica set rs0, kafka creates its topics, kafka-connect uploads the Debezium connector configs once healthy, and phs waits for those before starting. A few restarts on the kafka side in the first minutes are normal — the entrypoints' wait-loops sort themselves out.

The base service builds from Dockerfile.development and bind-mounts your checkout, so the API hot-reloads when you edit the Go source — this compose file doubles as the development environment.

What's listening where

ServiceURL / port
API (base)http://localhost:12365
Web UI (www)http://localhost:3000
pvr repo servicehttp://localhost:12367
nginx TLS fronthttps://localhost:12376 (http on 12375)
mongo primarymongodb://localhost:27017
kibana (optional)http://localhost:5601

Open the UI at http://localhost:3000 and log in with a demo account — in dev mode the built-ins are enabled with their defaults (e.g. admin/admin). To point the pvr CLI at your cluster:

sh
pvr -b http://localhost:12365 login

Two optional extras:

  • Log UI — kibana sits behind a compose profile: docker compose --profile logs up -d.
  • Mutual TLS — the nginx service on :12376 terminates TLS and forwards the client certificate to the API (Pantahub-TLS-Client-Cert header). This lets you exercise the TLS onboarding protocol with local clients (e.g. curl --cert) — not with real devices, which would reject the self-signed certificate.

Simulating devices

Since real hardware can't join a development Hub, pantavisor-mocker fills the gap: it simulates a Pantavisor device — registration with an auto-join token, device/user metadata sync, the full OTA update lifecycle (download, install, test, rollback), log upload, and a pvcontrol- compatible socket — without any hardware.

sh
# create a device token in your local Hub first (Device tokens → new)
pantavisor-mocker init --storage dev-device-1 \
  --host localhost --port 12365 \
  --token <auto-join token>

pantavisor-mocker start --storage dev-device-1 --auto

--host/--port point the simulated device at your local API instead of the api.pantahub.com default; they land in the storage's pantahub.config (PH_CREDS_HOST / PH_CREDS_PORT), where you can also change them later.

--auto makes it accept updates on its own, so the simulated device walks trails like a healthy real one — useful for exercising deployments end to end. Each --storage directory is one device; run several to fake a small fleet. (Also available as a container: ghcr.io/pantavisor/pantavisor-mocker.)

The other compose files

FilePurpose
docker-compose.ymlThe full stack described here — the default.
docker-compose.development.ymlSlimmer dev variant without the kafka pipeline.
docker-compose.vs.ymlLike development, tuned for IDE/devcontainer use.
docker-compose.production.ymlMinimal single-node build (release Dockerfile, single mongo) — a starting point, not a hardened production setup.

For a real deployment — multi-node, and reachable by real devices — use the Helm chart with a public domain instead of stretching compose.

Reset

All state lives in .data/ and the elastic-data volume:

sh
docker compose down -v
rm -rf .data

Recreate .data/elasticsearch before the next up. Wiping kafka state but not zookeeper's (or vice versa) can cause cluster-id mismatches — when in doubt, wipe both.