ZenPlus Documentation

Install the ZenPlus appliance

One command on a fresh Ubuntu server builds a complete ZenPlus monitoring appliance — databases, poller, dashboard, web server and OTA updater — and brings it up with a 30‑day trial licence, ready to monitor immediately.

Applies to current install.sh (16-step) Updated 2026-08-11 Verified end-to-end Time 8–15 min Audience Sysadmins & NOC engineers

01 At a glance #

ZenPlus ships as a self-contained appliance. There is no cluster to design, no package matrix to resolve and no follow-up configuration script — the installer provisions every component, verifies that it actually came up, and prints exactly what to do next.

Install on a fresh Ubuntu server
$ curl -fsSL https://zentryc.com/install.sh | sudo bash

Safe to re-run at any time — the installer is idempotent and preserves data, credentials and licences.

Delivery
Single Bash installer over HTTPS
Supported OS
Ubuntu 20.04 / 22.04 / 24.04 LTS
Install time
8–15 min on a clean server
Steps
16 verified phases, all idempotent
Out of the box
30-day trial, every feature unlocked
Inbound ports
80 / 443 on the LAN only
What "verified" means

Each of the 16 steps prints a single line and earns its checkmark only after its post-conditions actually hold — the binary exists, the database answers, the API returns healthy. The installer never reports success over a broken install: it stops at the first failure of a critical step, shows the last 25 lines of that step's output, and exits non-zero.

02 Requirements #

A dedicated virtual machine or physical server is recommended. The appliance runs its own PostgreSQL, Redis and ClickHouse instances, so a host that already runs those services will need manual cleanup first.

ResourceMinimumRecommendedWhy it matters
Operating systemUbuntu 20.04 LTSUbuntu 22.04 or 24.04 LTSServer edition; other distributions are rejected at preflight
Architecturex86_64 / amd64x86_64 / amd6432-bit and ARM hosts are rejected
CPU2 cores4 coresThe Go poller is highly concurrent; ClickHouse scales with cores
RAM4 GB8 GB or moreClickHouse plus the build toolchain will thrash below this
Disk20 GB free100 GB or moreMetric history grows with device count and retention
Accessroot / sudoroot / sudoCreates system users, services and firewall-adjacent settings

2.1 · Outbound network access

Installation requires outbound HTTPS. The installer checks reachability before it writes anything and stops with a clear message if any host is unreachable — so a firewall problem costs you seconds, not ten minutes of a failed build.

DestinationPortPurposeNeeded
github.com, raw.githubusercontent.com443Application sourceInstall & updates
go.dev, dl.google.com443Go toolchainInstall
deb.nodesource.com443Node.js (dashboard build)Install
download.docker.com, Docker Hub443Docker engine + ClickHouse imageInstall & image upgrades
archive.ubuntu.com, pypi.org, registry.npmjs.org443 / 80System, Python and Node packagesInstall & updates
zentryc.com443Licence registration and OTA updatesContinuously, once registered
Behind a proxy

Export http_proxy and https_proxy before running the installer. If the proxy terminates TLS, it must also allow streaming responses from zentryc.com — the OTA agent uses chunked HTTPS for resumable downloads, and buffering proxies break it.

2.2 · Inbound (LAN) ports

PortListenerExposure
80 / 443nginx → dashboard, API and agent trafficYour LAN — the only ports users need
8000zenplus-api (FastAPI)Internal, proxied by nginx
5432 / 6379 / 8123 / 9000PostgreSQL / Redis / ClickHouseLoopback only

No inbound port from the public internet is required. Registration and OTA updates are outbound-only.

2.3 · The preflight gate

The first step rejects unsuitable hosts in seconds rather than failing halfway through a build. It checks:

  • OS family and release — Ubuntu 20.04 or newer; anything else is a hard stop, Debian raises an "untested" warning.
  • CPU architecture — must be amd64 / x86_64.
  • Memory and free disk — against the minimums above, on the filesystem that will hold /opt/zenplus.
  • Port conflicts — ports 80, 8000, 5432, 6379, 8123 and 9000 are inspected; unknown listeners raise a warning, ZenPlus's own services do not.
  • Outbound HTTPS — a live reachability probe of every download host.

03 Run the installer #

On a fresh Ubuntu server, as root or with sudo:

curl -fsSL https://zentryc.com/install.sh | sudo bash

Equivalent, straight from the source repository:

curl -fsSL https://raw.githubusercontent.com/khuram2025/zen-mon/main/install.sh | sudo bash

Installation takes 8–15 minutes on a typical server — most of that is compiling the Go poller and building the React dashboard. Prefer to read before you run? Download first, inspect, then execute:

curl -fsSL https://zentryc.com/install.sh -o /tmp/zenplus-install.sh
less /tmp/zenplus-install.sh
sudo bash /tmp/zenplus-install.sh

3.1 · What you will see

The console stays a clean checklist. Every step reports one verified line, with the time it took:

root@mon-01: ~ — zenplus installer
  ╔══════════════════════════════════════════════════════════╗
  ║   ZenPlus — Network Monitoring Appliance                  ║
  ║   Automated Installer                                    ║
  ╚══════════════════════════════════════════════════════════╝

  Host      mon-01 (10.20.30.40)
  Source    zen-mon.git branch main
  Target    /opt/zenplus
  Log       /var/log/zenplus-install.log

  This takes 8–15 minutes on a fresh server. Safe to re-run.

  [01/16] Validating host                             (2s)
  [02/16] Installing system packages                  (3m14s)
  [03/16] Creating service account                    (0s)
  [04/16] Fetching application source                 (18s)
  [05/16] Generating configuration                    (0s)
  [06/16] Building application                        (5m02s)
  [07/16] Initialising databases                      (1m11s)
  [08/16] Provisioning trial licence                  (0s)
  [09/16] Configuring services                        (12s)
  [10/16] Installing TLS/security tools               (1s)
  [11/16] Configuring OTA updater                     (3s)
  [12/16] Installing support tooling                  (2s)
  [13/16] Installing management CLI                   (0s)
  [14/16] Finalising installation                     (1s)
  [15/16] Verifying appliance health                  (14s)
  [16/16] Collecting system report                    (1s)

  Installation summary
  ──────────────────────────────────────────────────────────
     16 steps · 0 warnings · completed in 10m21s
  ──────────────────────────────────────────────────────────

  ✓ ZenPlus 1.11.0 is installed and running.

  Access
    Dashboard    http://10.20.30.40
    API docs     http://10.20.30.40/docs
    Sign in      admin / admin123   (change this immediately)

  Licence
    Trial active — expires 2026-09-10
    50 devices · 20 service checks · 5 users

Everything each step prints is captured in /var/log/zenplus-install.log, with ===== [n/16] Step title ===== markers. The console stays readable; the log holds the full detail.

3.2 · If a step fails

The installer stops at the first failure of a critical step, prints the last 25 lines of that step's output, and exits non-zero. Non-critical components — the TLS helper, OTA updater and support tooling — degrade to a warning and the install continues, leaving a working appliance with one feature to repair later.

Fix the reported cause and re-run the same command. The installer is idempotent: existing credentials, data directories, a changed admin password and any active subscription are all preserved.

04 The 16 steps, explained #

The steps run in a deliberate order — the application is built before the databases are initialised, because both the admin-hash repair and the trial seed need the Python virtual environment that the build step creates. Grouped by what they accomplish:

Prepare the host~3–4 min
01 Validating host 02 Installing system packages 03 Creating service account
Fetch & build~5–6 min
04 Fetching application source 05 Generating configuration 06 Building application
Data & licence~1–2 min
07 Initialising databases 08 Provisioning trial licence
Wire up the appliance~20 sec
09 Configuring services 10 TLS/security tools 11 OTA updater 12 Support tooling 13 Management CLI
Stamp & verify~15 sec
14 Finalising installation 15 Verifying appliance health 16 Collecting system report

Solid chips are critical — a failure aborts the install. Dashed chips are non-critical: a failure warns and the install continues.

#StepWhat it doesCritical
01Validating hostPreflight: OS, architecture, RAM, disk, port conflicts and outbound reachability — before anything is writtenYes
02Installing system packagesapt packages plus Docker, the Go toolchain and Node.js; enables non-root ICMPYes
03Creating service accountCreates the unprivileged zenplus system user and its directoriesYes
04Fetching application sourceClones or fast-forwards the repository into /opt/zenplus, preserving .env, data and backupsYes
05Generating configurationGenerates PostgreSQL, ClickHouse, Redis, JWT and SNMP-encryption secrets into .env (mode 0640). Existing values are kept on re-run.Yes
06Building applicationCompiles the Go poller (with cap_net_raw), creates the Python venv, and builds the React dashboardYes
07Initialising databasesCreates the PostgreSQL role and database, runs every schema migration, starts ClickHouse in Docker and applies its migrationsYes
08Provisioning trial licenceSeeds a 30-day trial with its entitlements — skipped if a subscription already existsYes
09Configuring servicesWrites the systemd units, the dependency-gate service and the nginx site, then enables and starts themYes
10Installing TLS/security toolsInstalls the certificate store and the privileged helper behind Settings → SecurityNo
11Configuring OTA updaterInstalls the updater service and timer, the polkit rule, the scoped sudoers entries and the default agent configNo
12Installing support toolingInstalls the tech-support bundle generator used by Zentryc supportNo
13Installing management CLIDrops the zenplus CLI at /usr/local/bin/zenplusYes
14Finalising installationStamps the semantic version into .version and fixes ownership across the treeYes
15Verifying appliance healthPolls the API health endpoint, confirms nginx serves the dashboard, and checks every service and containerYes
16Collecting system reportRecords a post-install snapshot into the log for later support bundlesNo
Why the version stamp matters

Step 14 writes a semantic version — not a git commit — to the first line of /opt/zenplus/.version. The OTA server matches releases on that value, so a commit hash there would silently break update matching. Confirm it with head -1 /opt/zenplus/.version.

05 First login #

When the installer finishes it prints the access details for the appliance:

Dashboard
http://<appliance-ip>
Served by nginx on port 80
API documentation
http://<appliance-ip>/docs
Interactive OpenAPI reference
Sign in
admin / admin123
Default credentials — change immediately
Change the admin password now

Go to Settings → General → Profile and set a real password before the appliance sees production traffic. Re-running the installer will not reset a password you have changed — it only repairs the stored hash if it is missing or corrupt.

06 Trial licence and registration #

A 30-day trial is provisioned automatically during installation. There is no activation step, no sales call and no feature gate — the appliance is fully usable the moment it boots.

30
Days
50
Devices
20
Service checks
5
Users

Every product capability is available during the trial — SNMP, NetFlow, NCM, APM, discovery, agents, sensors and reporting. Nothing is withheld. See pricing for the node bundles available after the trial.

6.1 · What a licence key unlocks

A licence key is required for exactly one thing: receiving over-the-air updates. Until the appliance is registered, update checks return an error and the Licenses tab shows "Appliance Not Registered". Monitoring is entirely unaffected.

Open Settings → General → Licenses

Signed in as an administrator on the appliance dashboard.

Paste the licence key and press Register

The key is supplied with your subscription. The appliance registers itself with zentryc.com over outbound HTTPS.

Confirm from the shell

Run sudo zenplus status — the registration state is printed alongside service health.

Re-running the installer never resets, shortens or extends an existing subscription.

07 Enable HTTPS #

A fresh appliance serves plain HTTP. For anything beyond a lab, enable TLS before adding devices or agents — one action secures the dashboard, the API and all agent and sensor traffic at once.

Go to Settings → General → Security and pick the path that matches your environment:

Self-signed certificate

Generated on the appliance in one click. Download the certificate and distribute it to clients — browser trust store, or Active Directory GPO to Trusted Root Certification Authorities — to avoid warnings.

Fastest

CSR signed by your CA

Generate a CSR on the appliance and have your enterprise CA sign it, then paste the issued certificate back. With AD Certificate Services, use the Web Server template.

Recommended

Upload an existing certificate

Bring your own PEM certificate and key, or a PKCS#12 / PFX bundle issued by your existing PKI or a public CA.

Bring your own

Then enable HTTPS and, optionally, the HTTP→HTTPS redirect, HSTS, and a TLS 1.2+/1.3-only floor. See the Zentryc security model for how this fits the wider platform.

After the switch

Changing to HTTPS changes the browser origin, so you will be asked to sign in again. Agents and sensors keep the controller URL they enrolled with — re-point existing ones at https:// and install the certificate into their host trust store.

08 Verify the installation #

Step 15 already asserted appliance health before the installer exited. To re-check at any time:

sudo zenplus status

Every service below should report active:

zenplus-wait-deps
zenplus-api
zenplus-poller
zenplus-updater.timer
nginx
postgresql
redis-server

ClickHouse runs in Docker and should report healthy:

docker ps --filter name=zenplus-clickhouse
zenplus-clickhouse   Up 3 minutes (healthy)

A direct API health check bypasses nginx entirely:

curl -fsS http://127.0.0.1:8000/api/v1/system/health
{"status":"ok","service":"zenplus-api"}

And confirm the installed version — it must be a semantic version:

head -1 /opt/zenplus/.version
The dependency gate

zenplus-wait-deps exists because PostgreSQL, Redis and ClickHouse boot asynchronously. Without an explicit gate, the API and poller would race the databases on every reboot. The gate blocks until all three answer health checks, then releases the API and poller together.

09 What gets installed #

Everything lands on one host. nginx is the only component listening to your network; the databases bind to loopback, the poller reaches out to your devices, and the updater reaches out to zentryc.com. Nothing calls in.

ZenPlus appliance architecture Users reach nginx on ports 80 and 443. nginx serves the React dashboard and proxies the FastAPI service on port 8000. The API and the Go poller share PostgreSQL, Redis and a ClickHouse container. The poller polls monitored devices over SNMP, ICMP and NetFlow. The OTA updater makes outbound HTTPS calls to zentryc.com. ZENPLUS APPLIANCE · ONE UBUNTU LTS HOST NOC users browser · API zentryc.com licence · OTA Monitored estate SNMP · ICMP · flow nginx :80 · :443 TLS from /etc/zenplus/tls zenplus-api FastAPI · 127.0.0.1:8000 Dashboard built React assets zenplus-poller Go · cap_net_raw PostgreSQL config, devices, users Redis cache, queues, sessions ClickHouse metrics & flow history Docker container zenplus-updater.timer checks for releases every 4 hours · outbound only zenplus CLI /usr/local/bin/zenplus poll & collect 443
Single-host ZenPlus appliance. nginx is the only inbound listener; the poller and the OTA updater are outbound-only.

9.1 · Filesystem and services

ComponentLocation
Application/opt/zenplus
Configuration & secrets/opt/zenplus/.env — mode 0640, owned by zenplus
Version marker/opt/zenplus/.version
OTA agent configuration/opt/zenplus/updater/config/agent.conf
PostgreSQLSystem service, database zenplus
ClickHouseDocker container zenplus-clickhouse
RedisSystem service, password-protected
Web servernginx → /etc/nginx/conf.d/zenplus.conf
TLS certificates/etc/zenplus/tls — root-owned, private key 0600
Serviceszenplus-api, zenplus-poller, zenplus-updater.timer, zenplus-wait-deps
Management CLI/usr/local/bin/zenplus
Install log/var/log/zenplus-install.log

9.2 · Privilege model

Nothing in ZenPlus runs as root beyond what the operating system itself requires:

  • The API and poller run as the unprivileged zenplus system user.
  • The poller sends ICMP echoes via the cap_net_raw file capability — a capability on the binary, not a root process.
  • Dashboard-triggered update actions flow through a scoped sudoers file at /etc/sudoers.d/zenplus-updater that enumerates the exact systemctl invocations permitted — nothing else — plus a polkit rule for the equivalent D-Bus calls.
  • Generated secrets live only in /opt/zenplus/.env at mode 0640; TLS private keys live in /etc/zenplus/tls at 0600, root-owned.

10 Managing the appliance #

The zenplus CLI is the day-to-day interface for anything that is not in the dashboard:

sudo zenplus status      # service and registration status
sudo zenplus update      # apply the latest release
sudo zenplus restart     # restart the application services
sudo zenplus start       # start everything, including databases
sudo zenplus stop        # stop the application services
sudo zenplus backup      # on-demand PostgreSQL dump
sudo zenplus logs api    # tail a log — api | poller | updater
CommandWhat it doesWhen to use it
statusPrints service state, container health and OTA registrationFirst stop for any "is it healthy?" question
updatePulls, migrates, rebuilds, reloads and restartsApplying a release manually, ahead of the OTA timer
restartRestarts the API, poller and nginxAfter a configuration change
backupDumps PostgreSQL into /opt/zenplus/backupsBefore a risky change or a manual upgrade
logsFollows the journal for a chosen unitDiagnosing a service that will not settle

10.1 · Files worth knowing

/var/log/zenplus-install.log          # complete installer output, per step
/opt/zenplus/.env                     # generated secrets and connection strings
/opt/zenplus/.version                 # semantic version + install timestamp
/opt/zenplus/updater/logs/update.log  # OTA agent activity
/etc/nginx/conf.d/zenplus.conf        # web server configuration

11 Staying up to date #

There are two update paths, and they lead to the same place.

Automatic — OTA
Every 4 hours

Once the appliance is registered, zenplus-updater.timer checks zentryc.com for a newer release, downloads it over resumable HTTPS, applies it, and reports the outcome back. Update history is visible in the dashboard.

Manual — CLI
sudo zenplus update

Pulls the current branch, runs any pending migrations, rebuilds the poller and dashboard, reloads nginx and restarts the services. Useful for air-gapped-adjacent environments or to apply a fix immediately.

Updates require registration

An unregistered appliance monitors perfectly but cannot receive OTA updates — update checks return "not registered". Paste the licence key in Settings → General → Licenses to enable the channel. See §06.

12 Troubleshooting #

Where to look first: /var/log/zenplus-install.log holds the complete output of every step, delimited by ===== [n/16] Step title ===== markers. Then:

sudo zenplus status
sudo systemctl status zenplus-api --no-pager
sudo journalctl -u zenplus-api -n 120 --no-pager -l
sudo journalctl -u zenplus-poller -n 120 --no-pager -l
docker ps

! Preflight rejects the host

Symptom
Step 01 fails immediately with a named reason and nothing is installed.
Cause
Unsupported OS or architecture, insufficient RAM or disk, or an unreachable download host.
Fix
These are hard requirements — the message names the specific failure. Behind a proxy, export http_proxy and https_proxy before running the installer, then re-run.

! Login returns HTTP 500

Symptom
The dashboard loads, but signing in returns a 500 and the API log shows a passlib UnknownHashError.
Cause
A malformed stored password hash. Current installers detect and repair this automatically on re-run.
Fix
Re-run the installer, or repair by hand — note the escaped $, since an unescaped $2b$ is expanded by the shell, which is what corrupts the hash in the first place:
sudo -u postgres psql -d zenplus -c \
"UPDATE users SET password_hash = '\$2b\$12\$vjHI8XBgL.dCyn.sgl41VufIFkQGcEzjt78GJdB66AwG9e9MZasai', is_active = true WHERE username = 'admin';"

That restores the password to admin123. Change it immediately afterwards.

! Poller fails with status=203/EXEC

Symptom
zenplus-poller refuses to start; systemd reports exit code 203.
Cause
The Go binary was never built — historically because Go VCS stamping failed on a repository flagged for "dubious ownership".
Fix
Current installers mark /opt/zenplus safe, build with -buildvcs=false, and fail the install if the binary is missing rather than continuing. To repair an existing appliance:
sudo bash -lc 'set -e
export PATH=/usr/local/go/bin:/usr/local/bin:$PATH
git config --global --add safe.directory /opt/zenplus || true
mkdir -p /opt/zenplus/bin
cd /opt/zenplus/poller
go mod download
CGO_ENABLED=0 go build -buildvcs=false -o /opt/zenplus/bin/zenplus-poller ./cmd/poller
chown zenplus:zenplus /opt/zenplus/bin/zenplus-poller
chmod 0755 /opt/zenplus/bin/zenplus-poller
setcap cap_net_raw+ep /opt/zenplus/bin/zenplus-poller || true
systemctl restart zenplus-poller'

! Settings → Security is unavailable

Symptom
The Security tab is missing or errors, so HTTPS cannot be configured from the dashboard.
Cause
The privileged TLS helper is not installed — expected on appliances built before the Security tab existed, or if step 10 warned.
Fix
Install it directly. Fresh installs and OTA updates do this automatically.
sudo bash /opt/zenplus/scripts/setup-security.sh
sudo systemctl restart zenplus-api

! Update checks report "not registered"

Symptom
The Licenses tab shows Appliance Not Registered and update checks return an error.
Cause
Expected on a trial or otherwise unlicensed appliance. Monitoring is unaffected; only OTA updates need registration.
Fix
Paste the licence key in Settings → General → Licenses, then confirm with sudo zenplus status.

! Dashboard loads but returns 502

Symptom
nginx serves the page shell, but API calls fail with a bad gateway.
Cause
The API is not listening on 8000 — usually a database that has not come up, so the dependency gate is still blocking.
Fix
Check the API directly and work backwards through the gate:
curl -fsS http://127.0.0.1:8000/api/v1/system/health
sudo systemctl status zenplus-wait-deps --no-pager
docker ps --filter name=zenplus-clickhouse
sudo journalctl -u zenplus-api -n 120 --no-pager -l

Still stuck? Contact Zentryc support and attach /var/log/zenplus-install.log — it contains everything the installer saw, step by step.

13 Advanced install options #

The installer reads a handful of environment variables, so you can change its behaviour without editing the script. Prefix them to the bash invocation:

curl -fsSL https://zentryc.com/install.sh | sudo ZENPLUS_BRANCH=release-1.11 bash
VariableDefaultEffect
ZENPLUS_BRANCHmainInstall a specific branch or release line
ZENPLUS_REPOZentryc repositoryInstall from a fork or an internal mirror
ZENPLUS_HOME/opt/zenplusInstall to a different filesystem — useful when /opt is small
INSTALL_LOG/var/log/zenplus-install.logWrite the install log elsewhere
TRIAL_DAYS30Length of the seeded trial licence
MIN_RAM_MB / MIN_DISK_GB3500 / 20Preflight thresholds — lower only for lab builds
Lowering the preflight minimums

MIN_RAM_MB and MIN_DISK_GB exist so lab and CI builds can proceed on small hosts. They do not make ZenPlus run well below the documented minimums — ClickHouse and the build toolchain will thrash. Do not lower them on a production appliance.

13.1 · Which version does a fresh install get?

install.sh clones the main branch and installs whatever version that branch carries. Cutting a release fast-forwards main to the released commit, so a fresh install always lands on the current release.

14 Publishing the installer Operators #

This section is for the team that runs zentryc.com, not for customers. The public one-liner is served as a static file and must not require login, API authentication or an access proxy.

In the server { server_name zentryc.com; … } block, before any generic application route:

location = /install.sh {
    root /var/www/zentryc-public;
    default_type text/plain;
    add_header Cache-Control "public, max-age=300";
    add_header X-Content-Type-Options "nosniff" always;
    try_files /install.sh =404;
}

Publish or refresh the file:

sudo mkdir -p /var/www/zentryc-public
sudo curl -fsSL https://raw.githubusercontent.com/khuram2025/zen-mon/main/install.sh \
  -o /var/www/zentryc-public/install.sh
sudo chmod 0644 /var/www/zentryc-public/install.sh
sudo nginx -t && sudo systemctl reload nginx

Verify from another machine:

curl -fsSIL https://zentryc.com/install.sh            # expect HTTP 200, text/plain
curl -fsSL  https://zentryc.com/install.sh | head -1  # expect #!/usr/bin/env bash
This copy does not update itself

The served file is a manual snapshot, so it drifts from the repository every time the installer changes. Refresh it as part of every release, and confirm the served copy targets the intended branch:

curl -fsSL https://zentryc.com/install.sh | grep ZENPLUS_BRANCH=

Ready to deploy ZenPlus?

One command, a 30-day trial with every feature unlocked, and no cluster to design. Talk to us about a production rollout, or start on a spare VM today.

Last updated 2026-08-11 · ZenPlus Installation Guide v2.0 ← ZenPlus knowledge base  ·  Contact support