Skip to main content

Docker Installation

From May 2026, the standard mass production/field deployment for PlantPulse Edge is a single Docker container image. Seven components (Tomcat / Cassandra / Redis / HiveMQ / TSE / Dashboard / Node-RED) are contained within one image. install.sh handles everything from OS dependencies to systemd registration, after which plantpulse-edge.service manages the container.

Quick Install vs. This Page

For a summary of the procedure to quickly deploy a single unit in the field, see Quick Install (install.sh). This page is a reference that dives deep into what the installation does internally — image coordinates, docker run flags, bind mounts, EnvironmentFile, and OTA. For day-to-day operations, refer to Container Mode Operations.

Do not use simultaneously with native

1. Image Coordinates​

ItemValue
Registrydocker.kopens.io/pe (anonymous pull)
Image Nameplantpulse-edge
Full Coordinatesdocker.kopens.io/pe/plantpulse-edge:<tag>
Tag FormatYYYY.MM-YYYYMMDD-HHMM e.g., 2026.11-20261103-1006 (During installation, latest is also possible; OTA requires immutable tags only)
Download Serverhttps://product.kopens.io/plantpulse-edge/ (LAN alias)

Fixed paths within the image: KOPENS_HOME=/opt/kopens, PE_HOME=/opt/kopens/plantpulse-edge, PE_DATA_DIR=/data1, JAVA_HOME=/usr/lib/jvm/java-21-openjdk.


2. Installation Flow — bootstrap.sh → install.sh​

2.1 One-liner​

curl -fsSL https://product.kopens.io/plantpulse-edge/install.sh | sudo bash

install.sh is identical to bootstrap.sh. This script performs the following:

  1. Unpacks the repo tarball (plantpulse-edge-docker.tar.gz) to /opt/kopens/plantpulse-edge-docker/ (falls back to git clone upon failure)
  2. Applies PROFILE preset (if available, see Section 3 below)
  3. Executes the 9 steps of the install.sh main body
⌨️ The installation «asks items» — this shows scenes where the user is asked again if a value is left blank. This is not a screen of an actual installation run (to avoid configuring the recording machine), but a reproduction by executing the prompt library used by the installer. «Behaviors» like re-asking are real, and the item list is kept in sync with the installer declaration via CI to prevent divergence.
«Asked items» vary by mode — Default is EDGE

If executed directly in the terminal, it asks for input (default behavior). Values in brackets are defaults and are accepted by pressing Enter.

  • EDGE (Default) — Must ask for the platform address and API key; will ask again if left blank.
  • STANDALONE — Does not ask for either of those.
  • Edge id Defaults vary by box (follows the EDGE_3F8A21 format derived from NET1 MAC). Enter the field number here if applicable.
Cannot ask via curl | bash — provide values as environment variables

When executed via pipe, the terminal is not attached, so the script cannot ask for input. In such cases, it states that it «cannot ask» and proceeds using environment variables and default values (it does not pass silently).

curl -fsSL https://product.kopens.io/plantpulse-edge/install.sh | \
sudo EDGE_ID=EDGE_00303 DEV_MODE=EDGE \
SERVER_HOST=<플랫폼IP> SERVER_API_KEY=<token> bash

To enable interactive prompts, download and execute the script directly. For automation (CI/unattended installation), it is safer to disable prompts using INTERACTIVE=0.

Operators only need to modify env.sh

Write it in env.sh in the same folder as the script, and the installation will read it automatically. The same value will be used during re-installation.

2.1a «Verification» of Platform Key During Installation​

If DEV_MODE=EDGE is enabled, the installer actually queries the platform before writing the settings. Previously, it only checked if the value was non-empty, and all connections failed with 401 errors only after booting.

ScenarioInstaller Behavior
Platform unreachableWarning only — The platform may not be up yet, or the firewall may not be configured
Server 5xxWarning only — Indicates a failure to determine the status, not a key issue
401 · 403🔴 Halt — Key rejected. Typos are caught here
400🔴 Halt — Issue with the installer; please report this

If the platform must be installed after the Edge, disable this with SKIP_PLATFORM_VERIFY=1.

2.1b --help is the Official Usage Guide​

sudo bash install.sh --help # root 없이도 읽힙니다
⌨️ Full text of `--help` — Loaded as a «still image» since the output is printed all at once in 0.04 seconds. This recording comes from the «same release execution» as the installer, reflecting the screen of the current script.

The output includes a banner followed by sections USAGE · MODE decides what else is required · IDENTITY · PLATFORM · IMAGE · NETWORK · SKIP FLAGS · PROFILE · AFTER INSTALL. The full text is not transcribed here because transcribed copies quietly become outdated. There were previous instances where the headers in this repository diverged from the default values in the code.

2.2 install.sh 9 Steps​

#Step
1OS Detection (Rocky/Alma/RHEL/Ubuntu/Debian)
2Docker Installation (dnf or apt)
3Directory creation for /data1, /etc/kopens, /var/log/kopens
4sysctl + firewalld ports + chrony + hostname + nmcli static IP
5Creation of /etc/kopens/app.properties (EDGE_ID / SITE_ID / random credentials per box)
6systemd unit installation — plantpulse-edge.service + OTA kopens-upgrade.path/.service
7docker pull (cosign signature verification — warning only during install, halt if verification fails during upgrade)
8systemctl enable --now plantpulse-edge.service
9Registration of weekly cron cleanup

During the first installation, the admin / MQTT / OPC-UA / API key / keystore passwords are generated randomly per box and stored once in /etc/kopens/credentials.txt (chmod 0600 root).


3. PROFILE Preset​

Apply default values for each site type at once using the PROFILE environment variable.

PROFILEApplied Defaults
productionDEV_MODE=EDGE (Requires explicit platform SERVER_HOST/SERVER_API_KEY)
stagingDEV_MODE=EDGE, EDGE_ID=EDGE_00303, SITE_ID=SITE_00001, SERVER_HOST=192.168.0.235, SKIP_COSIGN_VERIFY=1
standaloneDEV_MODE=STANDALONE, SKIP_COSIGN_VERIFY=1 (Platform disconnected)
airgapIMAGE_URL=tarball, SKIP_PULL=1, DEV_MODE=STANDALONE (Air-gapped network installation)
# 양산 (플랫폼 연결)
curl -fsSL https://product.kopens.io/plantpulse-edge/install.sh \
| sudo PROFILE=production EDGE_ID=EDGE_00303 \
SERVER_HOST=10.0.0.5 SERVER_API_KEY=<uuid> bash

# 단독 운영 (플랫폼 없이)
curl -fsSL https://product.kopens.io/plantpulse-edge/install.sh \
| sudo PROFILE=standalone EDGE_ID=EDGE_LOCAL_01 bash
staging = dev clone

The staging box is a clone of dev with the same EDGE_00303 / SITE_00001. The only difference should be the platform connection info (SERVER_HOST=192.168.0.235 / API key). Using a site not registered on the platform will cause the Edge self-diagnostic OPC to fail due to an FK violation, so use the registered EDGE/site. If connected to the platform simultaneously with dev, they will be recognized as the same edge; operate them so that verification does not overlap.

For a full list of environment variables commonly used during installation, see env Configuration §6.


4. systemd Unit Anatomy — plantpulse-edge.service​

This is the actual container startup definition deployed by install.sh as /etc/systemd/system/plantpulse-edge.service.

4.1 docker run (ExecStart)​

/usr/bin/docker run --rm --name plantpulse-edge \
--network=host \
--init \
--stop-timeout=120 \
-v /data1:/data1:z \
-v /etc/kopens:/etc/kopens:z \
-v /etc/kopens/conf:/opt/kopens/plantpulse-edge/conf:z \
-v /var/security:/var/security:rw,z \
-v /etc/localtime:/etc/localtime:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PP_LANG=${PP_LANG} \
-e PP_TZ=${PP_TZ} \
-e CERT_PASS=${CERT_PASS} \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--cap-add=NET_RAW \
--cap-add=DAC_OVERRIDE \
--cap-add=CHOWN \
--cap-add=FOWNER \
--log-driver=journald \
--log-opt tag="plantpulse-edge" \
${KOPENS_IMAGE}:${PE_VERSION}
FlagMeaning
--network=hostAutomatically expose 9 ports + natural multicast / OPC-UA discovery
--inittini as PID 1 (zombie reap + signal forward)
--stop-timeout=120Ensures time for Cassandra graceful drain
--cap-drop=ALL + individual --cap-addMinimum privilege. NET_RAW for PLC ping, NET_BIND_SERVICE for 80/443 bind

4.2 Bind Mount​

Host PathContainer PathModePurpose
/data1/data1rw,zData (Cassandra/Redis/HiveMQ/Node-RED) — preserved across container replacement
/etc/kopens/etc/kopensrw,zConfiguration + signal files
/etc/kopens/conf/opt/kopens/plantpulse-edge/confrw,zapp.properties / log4j2.xml
/var/security/var/securityrw,zTLS keystore
/etc/localtime/etc/localtimeroHost timezone
/var/run/docker.sock/var/run/docker.sockrw/ui/apps Docker app management

4.3 EnvironmentFile + Defaults​

Built-in unit defaults (when file is missing):

Environment=PE_VERSION=latest
Environment=KOPENS_IMAGE=docker.kopens.io/pe/plantpulse-edge
Environment=PP_LANG=en
Environment=PP_TZ=Asia/Seoul
Environment=CERT_PASS=kopens123!

EnvironmentFile (overrides if present — latest value takes priority):

FileKeyCreator
/etc/kopens/version.envPE_VERSION=<tag>OTA upgrade.sh
/etc/kopens/i18n.envPP_LANG / PP_TZinstall.sh
/etc/kopens/cert.envCERT_PASSTLS issuance script

4.4 Resource Limits / Restart Policy​

MemoryMax=12G # 16GB 박스 기준 — 호스트 OOM 방지
MemoryHigh=11G
CPUQuota=600% # 6 cores
TasksMax=8192

Restart=on-failure
RestartSec=30s
TimeoutStartSec=300
TimeoutStopSec=180

For heap reduction overrides (drop-ins) for 8GB boxes, see Container Mode §Resource Limits.


5. Container Runtime Configuration Read Paths​

In WAR mode (from 2026-06-13~), configurations are read directly from /etc/kopens on the host — no image rebuild required.

/etc/kopens/app.properties ← 운영자가 편집하는 canonical
│ (entrypoint 가 symlink 생성)
▼
/etc/kopens/conf/app.properties ──bind──▶ /opt/kopens/plantpulse-edge/conf/app.properties
▲
Tomcat setenv.sh: -Dpe.conf.dir=/opt/kopens/plantpulse-edge/conf
  • app.properties: Host /etc/kopens/app.properties is the source-of-truth. If missing, the entrypoint seeds it with the image default. Operator edits → reflected via config.sh --restart.
  • log4j2.xml: The entrypoint copies the webapp default to conf/log4j2.xml at every boot (code artifact — change log level and restart without image rebuild).
  • HiveMQ auth.properties: The entrypoint automatically syncs mqtt.server.* of app.properties at every boot — only app.properties needs to be modified.
  • TLS Password: The entrypoint interprets CERT_PASS from cert.env and bulk-patches the Tomcat server.xml / HiveMQ config / OPC-UA keystore passwords.

For detailed env→configuration mirroring, see Environment Variable Configuration. For the meaning of each key, see app.properties Reference.


6. OTA Upgrade / Rollback​

The version is fixed in /etc/kopens/version.env — OTA replaces this file with a verified digest and restarts.

# 자동 (META-INF/VERSION.JSON 이 가리키는 빌드)
sudo bash /opt/kopens/install/upgrade.sh

# 특정 불변 태그 (YYYY.MM-YYYYMMDD-HHMM). 움직이는 태그는 거부합니다
sudo bash /opt/kopens/install/upgrade.sh --to 2026.11-20261103-1006

# 진행 중인 현재 업그레이드만 롤백 (trial · rollback_failed 상태에서만)
sudo bash /opt/kopens/install/upgrade.sh --rollback

# 사전 점검만 (0 최신, 10 새 빌드 있음)
sudo bash /opt/kopens/install/upgrade.sh --check

Status: cat /etc/kopens/upgrade.status → idle · downloading · verified · snapshot · switching · trial · committed · rolling_back · rolled_back · rollback_failed. For detailed flow, see Upgrade.


7. Manual docker run for Debugging​

Operations must always go through systemd / install.sh, but manual startup is used for isolated debugging:

sudo docker run --rm --name pe-debug \
--network=host --init \
-v /data1:/data1:z \
-v /etc/kopens:/etc/kopens:z \
-v /etc/kopens/conf:/opt/kopens/plantpulse-edge/conf:z \
-v /var/security:/var/security:rw,z \
-e PP_LANG=en -e PP_TZ=Asia/Seoul -e CERT_PASS="$(. /etc/kopens/cert.env; echo $CERT_PASS)" \
docker.kopens.io/pe/plantpulse-edge:<tag>

Deployments must only occur via pipeline — do not perform image (re)build / push / staging redeployment locally. Use the release chain of plantpulse-edge-docker (edge:libs → edge:deploy-dev → edge:image → edge:staging → edge:version, triggered by pipeline variable RELEASE=1). The above command is for isolated debugging only.


8. Post-Installation Verification​

systemctl is-active plantpulse-edge.service
sudo bash /opt/kopens/install/bin/status.sh

# version: container_mode=true 여야 함
curl -ks https://127.0.0.1/api/v1/system/version | python3 -m json.tool
# health: HTTP 200 + components UP
curl -ks https://127.0.0.1/api/v1/system/health | python3 -m json.tool
ScreenURL
Web UIhttps://<gateway-ip>/ui/main
Node-REDhttps://<gateway-ip>/ui/flow/
Swagger UIhttps://<gateway-ip>/public/swagger-ui/index.html

For action by symptom, see Installation Troubleshooting. For day-to-day operations/failures, see Container Mode Operations.


9. Next Documents​