Skip to main content
Version: v2 (current)

Install PanDev Metrics on-prem

TL;DR. PanDev Metrics on-prem ships as a distribution archive containing a ready-to-run Docker Compose stack and a bundled Helm chart. For a production Docker Compose install, first provision a Linux VM, publish only Nginx / reverse proxy on 443, then configure .env and start the stack. For Helm, configure values.yaml and Ingress through the bundled chart. Audience: admin.

Before you begin

Pick one deployment path and have its prerequisites ready, plus the distribution archive:

  • Docker Compose path — a single Linux VM or host with Docker Engine ≥ 20.10 and Docker Compose ≥ v2.0 (v2.20+ recommended).
  • Helm path — a Kubernetes 1.28+ cluster with Helm 3 and kubectl configured for it.

In both cases you also need:

  • The PanDev Metrics distribution archive from your account manager.
  • An x86_64 host (default image tags) or an arm64 host (-arm tags) — see system requirements for the CPU-instruction baseline.
  • Outbound HTTPS to your Git provider and task tracker — egress is minimal but cannot be disabled.
  • A reverse proxy for the installation. Do not expose the stack's ports (8080/8090) directly to users — publish only the reverse proxy over TLS (443) and keep the container ports behind it. You'll need reachable UI/API hostnames and a TLS certificate trusted by browsers and IDE plugins.
  • Mutual reachability with integrations. If you connect cloud Git providers or task trackers, the backend's public URL must also be reachable from them so their webhooks deliver (real-time sync). Integration connectivity is two-way — see Network and ports.

For Docker Compose, prepare the VM and reverse proxy before editing .env. The workspace reads API_BASE_URL at startup and bakes that value into the browser-facing UI, so changing the public API name later requires recreating the workspace container.

What you get in the archive

Your account manager provides a distribution archive. Unpack it into a working directory:

terminal
sudo mkdir -p /opt/pandev
sudo chown "$USER":"$USER" /opt/pandev
cd /opt/pandev
unzip /tmp/pandev-metrics-distribution.zip -d .

The archive contains two self-contained deployment options plus configuration:

.
├── docker-compose/
│ ├── .env # the file you edit
│ └── docker-compose.yml # server + workspace + PostgreSQL
└── helm-chart/
├── Chart.yaml
├── values.yaml # all Helm parameters, with comments
├── README.md
└── templates/

You do not need to install or pre-provision PostgreSQL separately — it is part of both the Compose stack and the Helm chart. Choose one deployment path:

  • Docker Compose — a single host with Docker. The simplest path and the default for most installs.
  • Helm / Kubernetes — an existing Kubernetes cluster (1.28+).
warning

PanDev Metrics on-prem is a single-organization deployment. Do not plan around multi-tenant separation — that capability is Cloud-only. Air-gapped deployments are not supported: the backend needs minimal outbound HTTPS to your Git provider and task tracker.

The three components

Both deployment paths run the same three images:

ComponentImagePortRole
Server (backend)pandevofficial/pandev-metrics8080REST API for the UI and IDE plugins; runs Flyway migrations on startup
Workspace (frontend)pandevofficial/pandev-metrics-backoffice8090 → container 80The web UI; a static React bundle served by Nginx
PostgreSQLpostgres:16 (Compose) / public.ecr.aws/bitnami/postgresql:16 (Helm)5432System of record for all persistent data

The bundled PostgreSQL is version 16; the backend is also compatible with PostgreSQL 17 if you supply an external or managed database.

The backend is a native image built with GraalVM, so it is published per CPU architecture. The default tags target x86_64; images with the -arm suffix target arm64 (including Apple Silicon). Pick the tag that matches your host — see system requirements for the exact CPU-instruction baseline.


Deploy with Docker Compose

Step 1 — Prepare the VM and reverse proxy perimeter

Create the application VM first. Size it from System requirements, use a supported Linux distribution, and make sure the hypervisor exposes the required CPU instructions if the VM is x86_64.

Before unpacking the distribution archive:

  • Assign a stable private IP address to the VM.
  • Create DNS records for the public names you will expose, for example app.example.com for the workspace and metrics.example.com for the backend API.
  • Make sure those names resolve from user browsers, IDE plugin hosts, and any connected providers that deliver webhooks. Cloud Git providers and task trackers can deliver webhooks only if the backend API name is reachable from them.
  • Install Docker Engine, Docker Compose, unzip, and Nginx.
  • Open inbound 443 to Nginx. Do not publish 8080, 8090, 5432, or 9090 as user-facing endpoints.
  • Install the TLS certificate that matches the UI and API hostnames.

When Nginx runs on the same VM as Docker Compose, it should proxy to http://127.0.0.1:8090 for the workspace and http://127.0.0.1:8080 for the backend. That keeps the public surface to 443 while still allowing local health checks and logs on the VM.

Step 2 — Review the Compose file

Switch into the Compose directory and look at what will run:

terminal
cd /opt/pandev/docker-compose

docker-compose.yml defines three services on a private bridge network. The server reads its database connection from the POSTGRES_* variables, the workspace reads API_BASE_URL, and PostgreSQL persists data to a named volume (postgres-data) so it survives restarts:

docker-compose.yml (excerpt)
services:
pandev-metrics-server:
image: pandevofficial/pandev-metrics:<version> # exact tag pinned in the shipped file
ports: ["8080:8080"]
environment:
DB_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB}
DB_USERNAME: ${POSTGRES_USER}
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_SCHEMA: public

pandev-metrics-workspace:
image: pandevofficial/pandev-metrics-backoffice:<version>
ports: ["8090:80"]
environment:
API_BASE_URL: ${API_BASE_URL}

postgres:
image: postgres:16
ports: ["5432:5432"]
volumes: [postgres-data:/var/lib/postgresql/data]

Notice that the server's DB_URL, DB_USERNAME, and DB_PASSWORD are assembled automatically from the POSTGRES_* values you set in .env. You do not edit docker-compose.yml for a standard install.

Step 3 — Configure the .env file

The archive ships a ready .env file in the docker-compose/ directory. As delivered it contains evaluation defaults — this is the complete set of variables, there is nothing else to configure:

docker-compose/.env (as shipped)
# --- PostgreSQL ---
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres

# --- Frontend ---
API_BASE_URL=http://localhost:8080

For the installation, change at least the database password and the public API URL. Use the API hostname that Nginx will publish, not a Docker service name:

docker-compose/.env (edited for a real install)
POSTGRES_DB=pandev_metrics
POSTGRES_USER=pandev
POSTGRES_PASSWORD=<STRONG_DB_PASSWORD>
API_BASE_URL=https://metrics.example.com
VariablePurposeNotes
POSTGRES_DBDatabase name created on first startAny valid identifier
POSTGRES_USERDatabase user the backend connects asCreated automatically inside the postgres container
POSTGRES_PASSWORDPassword for that userUse a strong value; this is the only DB secret
API_BASE_URLThe backend URL the browser uses to reach the APISee the note below — this is the one variable people get wrong

:::info About API_BASE_URL The workspace is a single-page app that runs in the user's browser. API_BASE_URL is baked into the UI as the address the browser calls for every API request — so it must be reachable from the user's machine, not from inside Docker.

Set it to the public URL of the backend, e.g. https://metrics.example.com (your reverse proxy). A Docker service name like http://pandev-metrics-server:8080 will not resolve in the browser. :::

warning

Treat .env as a secret: chmod 600 .env and keep it out of version control. The shipped defaults (postgres/postgres/postgres) are not production values — always change POSTGRES_PASSWORD before exposing the install.

Step 4 — (arm64 only) switch to the -arm images

If your host is arm64 (for example, Apple Silicon), edit docker-compose.yml and select the -arm tags, which are commented out next to the default x86_64 tags:

docker-compose.yml
image: pandevofficial/pandev-metrics:<version>-arm
# ...
image: pandevofficial/pandev-metrics-backoffice:<version>-arm

On x86_64 hosts, leave the default tags as shipped.

Step 5 — Pull images and start the stack

terminal
docker compose pull
docker compose up -d

On first start the backend runs Flyway migrations against the empty database, then boots. Expect 1–3 minutes before it is ready. Watch progress:

terminal
docker compose logs -f pandev-metrics-server

You will see org.flywaydb.core.internal.command.DbMigrate lines followed by the application startup banner.

Step 6 — Route the public names through Nginx

Terminate TLS at Nginx and route two names: the UI to the workspace (:8090) and the API to the backend (:8080). API_BASE_URL must match the public API name from this configuration.

/etc/nginx/conf.d/pandev.conf
# Workspace UI
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/ssl/certs/pandev.crt;
ssl_certificate_key /etc/ssl/private/pandev.key;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}

# Backend API
server {
listen 443 ssl http2;
server_name metrics.example.com; # matches API_BASE_URL
ssl_certificate /etc/ssl/certs/pandev.crt;
ssl_certificate_key /etc/ssl/private/pandev.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}

Validate and reload Nginx after writing the file:

terminal
sudo nginx -t
sudo systemctl reload nginx

See Network and ports for the full proxy, TLS, and firewall reference.

Step 7 — Verify

Check that all three containers are healthy. PostgreSQL has a built-in health check; the other two should be Up:

terminal
docker compose ps
NAME STATUS
pandev-metrics-server Up
pandev-metrics-workspace Up
postgres Up (healthy)

Open the workspace UI through Nginx, for example https://app.example.com. You should see the PanDev Metrics login screen, and the browser should reach the API at https://metrics.example.com without network errors.

The first administrator is created during initial setup — follow Licensing and first login.

note

Ports 8080 (backend API) and 8090 (workspace UI) are published to the host so Nginx can proxy to them locally. They do not need to be reachable from the public network. The Spring actuator (health/metrics) listens on port 9090 inside the container and is not published by the Compose file — use docker compose ps and the logs to judge backend health, not an external :9090 call. The shipped Compose file also publishes PostgreSQL on 5432; bind it to 127.0.0.1 or block it at the firewall — the backend reaches the database over the Docker network, so 5432 does not need to be exposed.


Deploy with Helm

The bundled chart in helm-chart/ deploys the same three components on Kubernetes 1.28+ (Helm 3). PostgreSQL is included as a StatefulSet, or you can point the chart at an external database.

Step 1 — Install

From the unpacked archive, install the chart and pass the public URLs for the API and the UI. The chart uses serverUrl and workspaceUrl to wire up Ingress hosts and to inject API_BASE_URL into the workspace automatically:

terminal
cd /opt/pandev

helm install pandev-metrics ./helm-chart \
--namespace metrics --create-namespace \
--set serverUrl=https://metrics.example.com \
--set workspaceUrl=https://app.example.com \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set postgresql.auth.password=<STRONG_DB_PASSWORD>

For anything beyond a quick trial, prefer a values.yaml file over long --set chains.

Step 2 — Key values

These are the parameters most installs touch. Run helm show values ./helm-chart for the complete, commented list — every parameter is documented there and in the helm-chart/README.md shipped in the archive.

ValueDefaultPurpose
serverUrl""Public URL of the backend; also becomes the workspace's API_BASE_URL
workspaceUrl""Public URL of the UI
ingress.enabled / ingress.classNamefalse / ""Create an Ingress and pick the controller (e.g. nginx)
server.image.tag / workspace.image.tagpinned per release in values.yamlImage versions; append -arm for arm64 nodes
postgresql.enabledtrueUse the bundled PostgreSQL StatefulSet
postgresql.auth.password""Password for the bundled database (generated if left empty)
postgresql.auth.username / .databasepostgres / pandev_metrics_dbUser and database name of the bundled PostgreSQL
postgresql.primary.persistence.enabled / .sizetrue / 100GiPersistent PVC for the database — enabled by default; see the note below
externalDatabase.*Used when postgresql.enabled=false

:::warning Persistence is on by default — make sure the PVC can bind The bundled chart ships with postgresql.primary.persistence.enabled=true and a 100Gi PVC, so database data already survives pod restarts. The catch is the StorageClass: if the cluster has no default StorageClass and you don't set one, the PVC stays Pending and the PostgreSQL pod never starts. Pin it explicitly when in doubt:

values.yaml
postgresql:
auth:
password: "<STRONG_DB_PASSWORD>"
primary:
persistence:
enabled: true # default
size: 100Gi # default; matches the system-requirements disk sizing
storageClass: "your-storage-class" # set if the cluster has no default StorageClass

Only set enabled: false for throwaway evaluation — it backs PostgreSQL with an emptyDir, and all data is lost on pod restart. :::

Step 3 — (optional) Use an external database

To run against a managed or external PostgreSQL instead of the bundled one, disable the built-in database and fill in externalDatabase:

values.yaml
postgresql:
enabled: false

externalDatabase:
host: "db.internal"
port: 5432
user: "pandev"
password: "<STRONG_DB_PASSWORD>"
database: "pandev_metrics_db"

Step 4 — Verify

terminal
kubectl get pods -n metrics
helm status pandev-metrics -n metrics

Pods become Ready as their probes pass — the backend pod on its management port (9090), the workspace and PostgreSQL pods on their own checks. The release notes printed by Helm tell you the resolved URLs. Then open workspaceUrl in a browser and complete Licensing and first login.


Upgrades and releases

PanDev Metrics ships new on-prem builds regularly. Upgrading is the same on both deployment paths: bump the image tags and let Flyway migrate the schema on startup. Take a backup first (see Backups and disaster recovery).

Check your current version

With Docker Compose, the running image tags are the version:

terminal
docker compose images

On Kubernetes:

terminal
helm list -n metrics
kubectl get deploy -n metrics -o jsonpath="{.items[*].spec.template.spec.containers[*].image}"

You can also read the version in the web UI footer once you sign in as an admin.

Where to find release notes

The detailed per-release changelog — with component versions, plugin versions, and download links — is published in its own section, not here:

Each entry lists backend (pandev-metrics) and workspace (pandev-metrics-backoffice) versions and Docker tags, the IDE / CLI / browser-extension versions, and the bug fixes and new features.

Upgrade with Docker Compose

  1. Read the release notes for the target version.
  2. Back up PostgreSQL with pg_dump (see Backups and disaster recovery).
  3. Update the image tags in docker-compose.yml for both the server and the workspace (use the -arm tags on arm64 hosts):
docker-compose.yml
services:
pandev-metrics-server:
image: pandevofficial/pandev-metrics:<NEW_SERVER_VERSION>
pandev-metrics-workspace:
image: pandevofficial/pandev-metrics-backoffice:<NEW_WORKSPACE_VERSION>
  1. Pull and restart:
terminal
docker compose pull
docker compose up -d
  1. Confirm the new tags with docker compose images and that the UI footer shows the new version.

Upgrade with Helm

  1. Back up PostgreSQL.
  2. Update the image tags in values.yaml:
values.yaml
server:
image:
tag: <NEW_SERVER_VERSION>
workspace:
image:
tag: <NEW_WORKSPACE_VERSION>
  1. Apply, using the chart from the unpacked archive:
terminal
helm upgrade pandev-metrics ./helm-chart -n metrics -f values.yaml
  1. Watch the rollout:
terminal
kubectl rollout status deployment/pandev-metrics-server -n metrics

Flyway applies any pending schema changes automatically on startup; you never run migrations by hand.

Backward compatibility

Upgrades are forward-only: install a newer build, the schema migrates in place, and you're done. Downgrades are not supported once migrations have run — restore from a backup if you need to roll back. Plugins (IDE, browser, CLI) are version-tolerant within the same major; update them at your own pace.

Backups and disaster recovery

A regular pg_dump of the PanDev Metrics database is sufficient — continuous archiving is not required.

terminal — bundled PostgreSQL (Compose)
docker compose exec postgres \
sh -c 'pg_dump -Fc -U "$POSTGRES_USER" "$POSTGRES_DB"' > pandev_metrics_$(date +%F).dump

The variables expand inside the postgres container (where Compose sets them), so the quoting matters — running pg_dump -U "$POSTGRES_USER" directly from the host would send empty values.

For an external database, run pg_dump from a host that can reach it. To recover, restore with pg_restore, then bring the stack back up — migrations align automatically on the next start.

Troubleshooting

Backend exits with FATAL: password authentication failed

POSTGRES_PASSWORD was changed after the database volume was already initialized. PostgreSQL only sets the password on the first start. Either set the password back to the original value, or reset the database: docker compose down -v (this deletes the postgres-data volume and all data) and docker compose up -d.

UI loads but every API call fails (blank dashboards, network errors)

API_BASE_URL is not reachable from the browser. It must be the public address of the backend, not a Docker service name. Fix it in .env and recreate the workspace: docker compose up -d --force-recreate pandev-metrics-workspace.

Backend container exits immediately with Illegal instruction (often on a VM)

The backend is a GraalVM native image and needs a baseline of x86_64 CPU instructions (AVX2, FMA, BMI2, …). A hypervisor masking host CPU features is the usual cause. Pass the host CPU through to the VM — see system requirements → virtualization. On arm64 hosts, use the -arm image tags instead.

Helm: PostgreSQL pod stuck in Pending

The database PVC cannot bind — usually because the cluster has no default StorageClass. List what exists with kubectl get storageclass, then set postgresql.primary.persistence.storageClass to one of them and helm upgrade. See the persistence note above.

Helm: database data disappears after a pod restart

Persistence is enabled by default (a 100Gi PVC), so this only happens if it was explicitly turned off — postgresql.primary.persistence.enabled=false backs PostgreSQL with an emptyDir. Set it back to true (with a size and, if needed, a storageClass), then helm upgrade.

FAQ

Do I need to install PostgreSQL myself?

No. PostgreSQL ships inside the distribution — as a container in Docker Compose and as a StatefulSet in the Helm chart. You only provide it externally if you deliberately choose postgresql.enabled=false (Helm) and point at your own instance.

Do I need to pre-create database tables?

No. The backend runs Flyway migrations automatically on first start and on every upgrade.

Does PanDev Metrics run on ARM / Apple Silicon?

Yes. Use the image tags with the -arm suffix (...:<version>-arm), which are native arm64 builds. The default tags are x86_64.

Can I disable outbound network access?

No. PanDev Metrics needs minimal outbound HTTPS to your Git provider and task tracker. Egress is minimal but cannot be disabled, and air-gapped deployments are not supported.

Which Kubernetes versions are supported?

Kubernetes 1.28+ with Helm 3. The chart and the Compose stack expose the same configuration shape.

How often do you release on-prem versions?

PanDev Metrics releases regularly across backend and plugins. Cadence is not fixed — minor versions land every few weeks, patches as needed. Check All releases for the recent rhythm.

Do I need to upgrade plugins together with the backend?

Not always. Plugins are version-tolerant — older plugins keep working with a newer backend within the same major version. To pick up new features (for example, AI activity tracking in the CLI), update the plugin from the version listed in the release notes.

Can I skip versions when upgrading?

Yes. Migrations chain forward, so jumping from 4.5.x to 4.7.2 runs every intermediate migration in order. Take a backup first and read the notes for all skipped versions to spot breaking changes.

How do I roll back a bad upgrade?

Stop the new containers, restore the PostgreSQL dump you took before the upgrade, then start the previous image tags. There's no in-place downgrade because schema migrations are forward-only.

Where do I download plugin builds?

Direct links to JetBrains, VS Code, and Xcode bundles are published with every release entry — see Current release. Host the artifacts on your internal mirror if your developer workstations route IDE traffic through a restricted egress that cannot reach cdn.pandev.io.

Next steps