Native Installation — Installing Directly on the Host Without Containers
This page covers how to install and operate PlantPulse Edge directly on the host OS (native), without Docker.
Each gateway runtime component (Tomcat / Cassandra / Redis / HiveMQ / Time-Series-Engine / Node-RED) runs as its own
host process, and a single plantpulse.service (systemd) manages the whole stack.
- Development / debug workstations — deploy code directly and validate quickly via JSP / class hot-swap
- Maintenance of pre-2026.05 legacy boxes — fields already running native
- Environments where security policy prohibits Docker
For new production deployments and single-unit field installs, the standard is container mode →
Quick Install (install.sh) / Docker (Container) Installation Details.
Native uses plantpulse.service, containers use plantpulse-edge.service.
Running both services at once causes conflicts on ports such as 80/443/1880/9042/12000 and on the /data1 data paths.
If systemctl is-active plantpulse-edge.service is active, that box is a container box —
be sure to stop/disable one side before bringing up native.
1. Runtime Model — What Runs and How
Native mode runs the seven components that make up the gateway as individual host processes.
Orchestration is handled by bash scripts under $PE_HOME/bin/, and each component has its own
bin/start.sh / bin/stop.sh in its directory.
| Component | Role | Directory |
|---|---|---|
Redis (cache) | Point queue (Redisson) | $PE_HOME/cache/ |
HiveMQ (mqtt) | MQTT broker / Sparkplug B | $PE_HOME/mqtt/ |
Cassandra (db) | Time series storage (pe keyspace) | $PE_HOME/db/ |
Time-Series-Engine (tse) | Time series engine | $PE_HOME/timeseries/engine/ |
| Dashboard | Dashboard | $PE_HOME/timeseries/dashboard/ |
Tomcat (server) | webapp plantpulse-edge-web (collection/REST/OPC-UA/UI) | $PE_HOME/server/ |
Node-RED (node) | Flows (/ui/flow) | $PE_HOME/node/ |
Boot order (by dependency) — bin/start.sh starts them in this order:
cache → mqtt → db → tse → dashboard → server → node
- Web UI and REST API served over HTTP 80 / HTTPS 443 (not 8080)
- JVM is JDK 21 (class file version 65) — Spring MVC 6.2 (non-boot)
- Graceful shutdown:
ServerStartListenercleans up collector → Redisson → OPC → Cassandra, thenRuntime.halt(0). A deadline watchdog (-Dplantpulse.edge.shutdown.deadline.ms, default 9000ms) prevents STOP_TIMEOUT races.
2. Directory Structure ($PE_HOME = /opt/kopens/plantpulse-edge)
$PE_HOME/
├── app/plantpulse-edge-web/ # webapp (WEB-INF/classes·jsp·lib + public)
├── bin/ # 오케스트레이션 스크립트 (아래 6장)
│ ├── start.sh / stop.sh # full stack — cache→mqtt→db→tse→dashboard→server→node
│ ├── restart.sh # Tomcat(server) + Node-RED 만
│ ├── lifecycle-lib.sh # pp_log / pp_wait_port / run_module_start 헬퍼
│ ├── upgrade.sh / firmware.sh / backup.sh / clean.sh / reboot.sh
│ └── log-viewer.sh / node-*.sh
├── conf/ # canonical 설정 (운영자 편집)
│ ├── app.properties # webapp 설정 (이 박스가 canonical)
│ └── env.sh # JAVA_HOME / PP_LANG / PP_TZ / PE_DATA_DIR …
├── server/ # Tomcat (bin/ conf/ logs/)
│ ├── bin/setenv.sh # JVM 옵션 / LOCALE / -Dpe.conf.dir
│ ├── conf/server.xml # Connector(80/443) / Context
│ └── logs/ # catalina.out / system.log / api.log / driver.log
├── cache/ → Redis (bin/start.sh / stop.sh)
├── db/ → Cassandra (bin/start.sh / stop.sh)
├── mqtt/ → HiveMQ (bin/start.sh / stop.sh)
├── timeseries/engine/ + dashboard/
└── node/ → Node-RED (userDir/node_modules/node-red-contrib-plantpulse-edge/)
Data directories are kept separate under $PE_DATA_DIR (default /data1) — Cassandra SSTables / Redis AOF / HiveMQ / Node-RED userDir.
3. Prerequisites (native)
| Item | Requirement |
|---|---|
| OS | Linux (RHEL/Rocky/Alma/Fedora family recommended) |
| Privileges | root (sudo -i) |
| JDK | OpenJDK 21 (dnf install java-21-openjdk java-21-openjdk-devel) — class file 65 compatible |
| Node.js | For Node-RED (nodejs / npm) |
| Python 3 | Helper scripts |
| Disk | /opt/kopens 3GB+, /data1 100GB+ recommended |
| Memory | 8GB minimum (Cassandra heap + Tomcat heap), 16GB+ recommended |
| NIC | Industrial appliance standard of 2 (1=WAN/external, 2=PLC/internal) |
| Time | NTP (chrony) synchronized |
A separate automated native installation tool handles OS packages, sysctl/limits, firewall, chrony,
NIC statics, SSL issuance, and systemd registration in one pass →
(legacy) Full H/W Installation (tools/setup.sh). This page covers the runtime
structure that tool lays down, plus manual/debug startup procedures.
4. Installation Procedure
4.1 Automatic (recommended) — tools/setup.sh
On a bare box right after OS boot, the automated installer aligns packages, network, tuning, JDK, and systemd in one pass. For the step-by-step inputs (hostname, NIC, firewall ports) and all 22 stages, follow (legacy) Full H/W Installation as written.
sudo -i
cd /opt/kopens/tools
./setup.sh # 대화형 — 호스트명 + NIC 입력 후 진행, 끝나면 10초 후 자동 reboot
After reboot, plantpulse.service starts the full stack automatically.
4.2 Manual / debug — bringing up the runtime only
When deploying just the runtime onto a box (or development workstation) that already has OS / JDK 21 / networking ready:
sudo -i
# 1) 런타임 배치를 $PE_HOME 에 펼침 (운영팀 제공 native bundle 기준)
# /opt/kopens/plantpulse-edge/{app,bin,conf,server,cache,db,mqtt,timeseries,node}
# 2) 환경 파일 확인 — JDK 21 / 언어 / 시간대 / 데이터 경로
cat $PE_HOME/conf/env.sh
# export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
# export PP_LANG="${PP_LANG:-en}" / export PP_TZ="${PP_TZ:-Asia/Seoul}"
# export PE_DATA_DIR=/data1
# (상세: env 환경 설정 페이지)
# 3) 메인 설정 — 사이트/플랫폼/DB/MQTT 값
vi $PE_HOME/conf/app.properties # edge.id / edge.site_id / server.host / cassandra.* …
# 4) 풀스택 기동 (cache→mqtt→db→tse→dashboard→server→node)
$PE_HOME/bin/start.sh
For details on the
env.sh/app.propertiesenvironment variable layers, see env Configuration. For the full set ofapp.propertieskeys, see the app.properties Guide.
5. systemd Integration — plantpulse.service
Once installation completes, systemd manages the full stack automatically.
sudo systemctl enable plantpulse # 부팅 시 자동 시작 등록
sudo systemctl start plantpulse # 시작 (ExecStart → service-start.sh → bin/start.sh)
sudo systemctl stop plantpulse # 정지 (ExecStop → service-stop.sh → bin/stop.sh)
sudo systemctl status plantpulse # 상태
sudo systemctl restart plantpulse # 풀스택 재시작 (60초+ 다운타임)
sudo journalctl -u plantpulse -n 100 # systemd 로그 마지막 100줄
Internal wiring:
plantpulse.service ─ ExecStart=service-start.sh ─→ bin/start.sh (cache→mqtt→db→tse→dashboard→server→node)
└ ExecStop =service-stop.sh ─→ bin/stop.sh
bin/restart.sh restarts only Tomcat (server) + Node-RED (~6 seconds, leaving Cassandra/Redis running).
Use it to apply app.properties edits or webapp updates.
systemd restart is a stop.sh → start.sh full-stack cycle and incurs 60+ seconds of downtime.
Details: Restart (restart.sh).
6. bin Script Catalog
| Script | Scope | Notes |
|---|---|---|
bin/start.sh | Full stack startup | cache→mqtt→db→tse→dashboard→server→node |
bin/stop.sh | Full stack shutdown | Calls each component's stop.sh; kill -9 if STOP_TIMEOUT_SECONDS (default 10s) is exceeded |
bin/restart.sh | Tomcat + Node-RED only | ~6 seconds, for applying code/config changes |
bin/backup.sh | Configuration backup | Backup Guide |
bin/upgrade.sh | Upgrade | Upgrade |
bin/clean.sh | Work directory cleanup | Cleanup |
bin/reboot.sh / firmware.sh | Host reboot / firmware | — |
bin/log-viewer.sh | Tail logs of all 7 components | Infinite tail -f — never call directly from automation/non-interactive SSH (session hang) |
Each run() terminates safely with a catch(Throwable) guard plus awaitTermination.
7. Post-Installation Health Check (1 minute)
# 1) systemd 서비스 살아있는지
systemctl status plantpulse # active (running)
# 2) 시스템 헬스 — HTTP 200 이면 게이트웨이 정상
curl -s http://127.0.0.1/api/v1/system/health | python3 -m json.tool
# 3) OPC-UA 트리 (등록 0 이어도 빈 배열이면 OK)
curl -s http://127.0.0.1/ui/opcua/tree | python3 -c 'import sys,json;print(len(json.load(sys.stdin)["data"]["tree"]))'
# 4) 웹 UI
# 브라우저 → https://<gateway>/ui/main (로고 + 카드가 보이면 정상)
Full log review — beyond catalina.out (boot failure) and system.log (ERROR/Exception),
check the logs of all 7 components (server/cache/db/mqtt/tse/dashboard/node) for SEVERE/ERROR entries.
In automation, read each logs/*.log in bounded fashion with tail -n / timeout instead of log-viewer.sh (infinite tail).
If ServerStartListener fails silently after a restart and the collector does not complete (OPC count 0), run restart.sh once more.
Note that data.monitor=null (+ data.api_client=null) in /api/v1/system/health is by design, not a race
(HealthResponse.livenessWithComponents leaves those two fields empty).
8. Common Pitfalls
| Symptom | Cause / Resolution |
|---|---|
UnsupportedClassVersionError (class file 65) | JDK 21 not installed or not specified. Check JAVA_HOME=/usr/lib/jvm/java-21-openjdk in env.sh |
| UI language differs from intent (ko/en) | -Duser.language in setenv.sh takes precedence over JAVA_TOOL_OPTIONS. See the LOCALE dynamic handling section of env Configuration |
connect ECONNREFUSED 127.0.0.1:80 | Tomcat not running. bin/restart.sh or bin/stop.sh+start.sh |
| Port/data conflicts | plantpulse-edge.service (container) is also active on the same box. Stop/disable one side |
kill -9 on shutdown | Cleanup (~10s) races with STOP_TIMEOUT_SECONDS (10s). Mitigated by the deadline watchdog. For a fully clean shutdown, use STOP_TIMEOUT 25 + deadline 20000 |
| Does not come up after reboot | Use journalctl -u plantpulse --no-pager to find the unit failure cause → run bin/start.sh directly and identify where it stalls |
9. Next Documents
- env Configuration —
env.sh/PP_LANG/PP_TZ/-Dpe.conf.dir - Docker (Container) Installation Details — production standard deployment
- (legacy) Full H/W Installation (
tools/setup.sh) — 22 stages of automated native installation - app.properties Guide — full set of main configuration keys
- Restart (
restart.sh) / Start (start.sh) / Stop (stop.sh) - Post-Installation Checklist / Production Readiness Criteria