Skip to main content

Docker (Container) Installation Details — plantpulse-edge.service

As of 2026.05, the standard production/field deployment of PlantPulse Edge is a single Docker container image. All 7 components (Tomcat / Cassandra / Redis / HiveMQ / TSE / Dashboard / Node-RED) are bundled in one image, with install.sh handling everything from OS dependencies to systemd registration, after which plantpulse-edge.service manages the container.

Quick install vs. this page

For a procedure summary to bring up a single box quickly in the field, see Quick Install (install.sh). This page is a reference that digs deep into what that installation does internally — image coordinates, docker run flags, bind mounts, EnvironmentFile, OTA. For day-to-day operation, see the Container Mode Operations Guide.

Do not run alongside native

The container is plantpulse-edge.service, native is plantpulse.service. The systemd unit carries Conflicts=plantpulse.service, but forcing both services up at once will cause port/data conflicts.


1. Image Coordinates

ItemValue
Registrydocker.kopens.io/pe (anonymous pull)
Image nameplantpulse-edge
Full coordinatedocker.kopens.io/pe/plantpulse-edge:<tag>
Tag format<VERSION>-<BUILD_DATE> e.g. 2026-20260524 (or latest)
Download serverhttps://product.kopens.io/plantpulse-edge/ (LAN alias)

Fixed paths inside 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.shinstall.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:

  1. Extracts the repo tarball (plantpulse-edge-docker.tar.gz) into /opt/kopens/plantpulse-edge-docker/ (falls back to git clone on failure)
  2. Applies the PROFILE preset (if present, see section 3 below)
  3. Interactive prompts — Edge ID (auto-suggested from MAC) / Site ID (default SITE_00001) / Mode (1=EDGE default / 2=STANDALONE) / for EDGE mode, platform server + API key
  4. Runs the 9 stages of install.sh itself

2.2 install.sh — 9 stages

#Stage
1OS detection (Rocky/Alma/RHEL/Ubuntu/Debian)
2Docker installation (dnf or apt)
3Create /data1, /etc/kopens, /var/log/kopens directories
4sysctl + firewalld ports + chrony + hostname + nmcli static IP
5Generate /etc/kopens/app.properties (EDGE_ID / SITE_ID / per-box random credentials)
6Install systemd units — plantpulse-edge.service + OTA kopens-upgrade.path/.service
7docker pull (cosign signature verification — soft-fail by default)
8systemctl enable --now plantpulse-edge.service
9Register weekly cron cleanup

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


3. PROFILE Presets

The PROFILE env applies a set of defaults for each site type in one shot.

PROFILEApplied defaults
productionDEV_MODE=EDGE (platform SERVER_HOST/SERVER_API_KEY must be specified)
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 (no platform connection)
airgapIMAGE_URL=tarball, SKIP_PULL=1, DEV_MODE=STANDALONE (Offline 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

A staging box is a clone of dev with the same EDGE_00303 / SITE_00001. The only things that must differ are the platform connection details (SERVER_HOST=192.168.0.235 / API key). Using a site that is not registered on the platform makes the edge self-diagnostic OPC fail with an FK violation, so use the registered EDGE/site as-is. If it connects to the platform at the same time as dev, both are recognized as the same edge, so schedule validation runs so they do not overlap.

For the full list of env vars commonly used at install time, see env Configuration §6.


4. Anatomy of the systemd unit — plantpulse-edge.service

This is the actual container startup definition that install.sh places at /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 exposes all 9 ports + makes multicast / OPC-UA discovery work naturally
--inittini runs as PID 1 (zombie reaping + signal forwarding)
--stop-timeout=120Allows time for a graceful Cassandra drain
--cap-drop=ALL + individual --cap-addLeast privilege. NET_RAW is for PLC ping, NET_BIND_SERVICE for binding 80/443

4.2 Bind mounts

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 time zone
/var/run/docker.sock/var/run/docker.sockrw/ui/apps Docker app management

4.3 EnvironmentFile + defaults

Defaults built into the unit (when no file is present):

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 when present — latest value wins):

FileKeyCreated by
/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 the heap-reduction override (drop-in) for 8GB boxes, see Container Mode §Resource Limits.


5. Container Runtime Configuration Read Paths

In WAR mode (from 2026-06-13), configuration is 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: the host's /etc/kopens/app.properties is the source of truth. If absent, the entrypoint seeds it from the image default. Operator edits → applied via config.sh --restart.
  • log4j2.xml: the entrypoint copies it from the webapp default to conf/log4j2.xml on every boot (build artifact — change the log level and restart, no image rebuild needed).
  • HiveMQ auth.properties: the entrypoint auto-syncs it with mqtt.server.* in app.properties on every boot — you only need to edit app.properties.
  • TLS passwords: the entrypoint resolves CERT_PASS from cert.env and patches the Tomcat server.xml / HiveMQ config / OPC-UA keystore passwords in one pass.

For details on env→configuration mirroring, see env Configuration; for the meaning of each key, see the app.properties Guide.


6. OTA Upgrade / Rollback

The version is pinned by a single PE_VERSION line in /etc/kopens/version.env — OTA only changes this file and restarts.

# 최신으로 업그레이드 (pre-snapshot → pull → restart → 90초 health probe → 실패 시 auto-rollback)
sudo bash /opt/kopens/install/upgrade.sh

# 특정 tag 로
sudo bash /opt/kopens/install/upgrade.sh --to 2026-20260601

# 직전 tag 로 rollback
sudo bash /opt/kopens/install/upgrade.sh --rollback

# 새 빌드 존재만 확인
sudo bash /opt/kopens/install/upgrade.sh --check

Manual rollback:

echo 'PE_VERSION=2026-20260524' > /etc/kopens/version.env
sudo systemctl restart plantpulse-edge.service

Status: cat /etc/kopens/upgrade.statusidle | running | success | rolling-back | rolled-back | error. For the detailed flow, see Container Mode §OTA.


7. Manual docker run for Debugging

Operations must always go through systemd / install.sh, but for isolated debugging you can start it manually:

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>

Deployment goes through the pipeline only — do not (re)build / push the image or redeploy staging locally; use the release chain in plantpulse-edge-docker (edge:libsedge:deploy-devedge:imageedge:stagingedge:version, triggered by the pipeline variable RELEASE=1). The command above 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 symptom-based remedies see Installation Troubleshooting; for day-to-day operation and failures see the Container Mode Operations Guide.


9. Next Documents