Skip to main content

Docker Installation

Overview​

This page provides a guide on how to perform a manual (step-by-step) installation of the PlantPulse Platform as a Docker Compose stack.

For general environments, the one-line installation is recommended. The one-line installation handles everything from package download to automatic environment detection, installation, and boot verification in one go. Please use the manual procedures on this page only in the following cases:

  • When each installation step must be directly reviewed and approved (e.g., security audits, change management procedures).
  • When environment variables (bin/env.sh) must be manually specified instead of using automatically detected values.
  • When installing in an air-gapped network environment where internet access is blocked → Air-gapped Installation.

Installation Structure — Nine Containers​

The Platform operates as a single docker compose stack. The master copy is compose/docker-compose.yml, and all operational scripts interact with the containers through this file.

ContainerLayerRole
plantpulse-certsCertificateBakes TLS materials and stays resident to monitor expiration. Healthy when (healthy)
plantpulse-datalakeInfrastructureStorage, messaging, analytics, CEP, SQL, monitor
plantpulse-server-webApplicationWeb console
plantpulse-batch-webApplicationBatch
plantpulse-warehouse-webApplicationData warehouse
plantpulse-plugin-opcuaApplicationOPC-UA server plugin (11004 / 11005)
plantpulse-plugin-aasxApplicationAASX server plugin
plantpulse-haApplicationRedundancy recovery daemon (10210)
plantpulse-proxyEdgeThe sole entry point for users (80 / 443 / 1883 / 1884)
There are eight always-on containers

It is normal if all nine appear as (healthy) in docker ps.

Additionally, plantpulse-mirror-maker for mirroring is defined in the compose file, but it is tied to the mirror profile and does not start by default.

The previous single-container configuration has been removed

Prior to 2026-08-29, a «monolith» configuration was available where all components ran together in a single plantpulse-platform container. This configuration has been deprecated, and the selection variable (PP_TOPOLOGY) and the corresponding compose file have been deleted. There is no longer a choice — bin/up.sh launches the above stack.

Commands such as docker logs plantpulse-platform found in old runbooks will not work as containers with those names no longer exist. Replacement commands can be found in the Operation Command Summary.

The primary purpose of separating containers by app is OOM isolation. If one app exhausts its memory, other apps and the infrastructure remain operational, and restarts or rollbacks can be performed at the app level.

Prerequisites​

Please verify the following before starting the installation.

ItemRequirement
Account Privilegesroot (or sudo privileges). The installation script configures OS settings and the Docker daemon
Operating SystemRHEL · Rocky Linux · Oracle Linux 8.x · 9.x · 10.x, Ubuntu 20.04+, Amazon Linux 2/2023
DockerCompose v2 is required (docker compose — the form without a hyphen). Install if install.sh is missing
Data DiskA large-capacity disk mount is recommended at the /data1 path — Docker data (/data1/docker-data) and Platform data volumes use this path
Registry AccessHTTPS access must be possible to docker.kopens.io (image registry) and product.kopens.io (installation package). For environments where access is blocked, please use Air-gapped Installation
Registry Credentialsdocker.kopens.io login credentials (issued by the KOPENS operations team)

Disk Path Guide: If a separate data disk is not available, the system will work by creating a /data1 directory on the root disk, but it is recommended to mount a dedicated disk to /data1 in production environments.

Manual Installation Procedure​

Step 1: Download Installation Package​

Download the installation package (tar.gz) from the download server and extract it to the standard path. Git cloning or installation of separate tools is not required.

sudo -i

# 표준 설치 경로 생성 후 패키지 다운로드 + 압축 해제
mkdir -p /opt/kopens/plantpulse-platform-docker
curl -fsSL "https://product.kopens.io/plantpulse-platform/plantpulse-platform-docker.tar.gz" \
| tar -xz -C /opt/kopens/plantpulse-platform-docker --strip-components=1

cd /opt/kopens/plantpulse-platform-docker/bin
chmod +x *.sh tools/*.sh

Two locations within the package are actually used:

LocationContents
bin/All installation and operation scripts
compose/Master stack docker-compose.yml and cluster worker overlay

Step 2: Preflight Inspection​

preflight.sh checks installation feasibility without making any changes to the system.

./preflight.sh

Inspection items:

  • Docker installation/daemon operation status (it is okay if not installed — install.sh will install it)
  • Existence of the Docker data-root parent path (/data1)
  • Existence of the secret sidecar (/etc/kopens/plantpulse-platform.env)
  • Usage status of major operational ports (80, 443, 4949, 4950)

Proceed to the next step only if OK appears. If ERROR is present, please refer to Troubleshooting. (WARN is for reference and does not block installation.)

Step 3: Review Environment Variables (env.sh)​

bin/env.sh determines the default values for the host side, and values changed by the operator are written to the sidecar /etc/kopens/plantpulse-platform.env. The values actually seen by the containers are determined by compose/docker-compose.yml — for detailed relationships, refer to the Environment Variable Reference.

vi /etc/kopens/plantpulse-platform.env
Do not modify the installation tree

Values written in bin/env.sh will be lost during reinstallation or upgrades. The installation script deletes and redownloads the installation tree.

Most resource values are «calculated» — there is little to change

env.sh automatically determines CPU, memory, and disk by looking at the host. Since these are not fixed defaults, appropriate smaller values are assigned to smaller boxes without manual intervention. The value specified per box always takes precedence.

VariableHow it is determinedWhen to modify
DOCKER_PP_CPUSnproc (if read fails, 8)When wanting to assign fewer cores to the container
DOCKER_PP_CLUSTER_CORESDOCKER_PP_CPUS - 2, min 4 · max 30Usually left as is
DOCKER_PP_MEMORY90% of host RAM, min 8GWhen wanting to leave more for the OS
DOCKER_DATALAKE_MEMORY96G (minimum). Truncated to not exceed 90% of host RAM — Hosts under 107G are insufficientOnly when increasing. Do not lower below 96G
DOCKER_PP_DATA_DISK_NAMEAutomatic reverse tracking of the actual disk supporting / (if failed, sda)When automatic detection is incorrect
PP_LANGenUse ko for Korean operations
PP_TZAsia/SeoulOverseas boxes
DOCKER_PP_EXTERNAL_IPEmpty valueOnly when the external public IP must be specified in a NAT environment
Do not enter an invalid IP in DOCKER_PP_EXTERNAL_IP

This value passes through PP_SERVICE_IP and enters the SAN list of the TLS certificate. If even one incorrectly formatted IP is mixed in, openssl will reject the entire extension file, resulting in no certificates being generated, and the stack will fail to start. If not behind a NAT, leave it empty — that is the default.

Current server values can be verified with the following command:

hostname -I | awk '{print $1}' # 서버 IP
free -g | awk '/^Mem:/{print $2"G"}' # 전체 메모리
nproc # CPU 코어 수
lsblk # 디스크 이름 (sda, sdb, nvme0n1 …)

In Korean operational boxes, typically only two lines are modified:

export PP_LANG=ko
export PP_TZ=Asia/Seoul
Locales are applied only at boot time

PP_LANG / PP_TZ are fixed at JVM boot time. To change them in a running stack, ./restart.sh is required. Additionally, since Aster TSDB time series are loaded as KST epoch, please maintain Asia/Seoul for Korean operations.

Step 4: Execute Installation (install.sh)​

sudo ./install.sh

A single install.sh automatically performs the entire installation process:

  1. OS Detection and System Configuration — File limits, kernel parameters (sysctl), time synchronization (chrony), SELinux permissive transition, swap disablement.
  2. Docker Engine Installation — Installed via OS-specific package manager + data-root configured to /data1/docker-data (Compose v2 installed if missing).
  3. Firewall Configuration — Opening public ports (80/443/4949/4950) and restricting internal ports to the private network range.
  4. Service Credential Generation — Creation of the /etc/kopens/plantpulse-platform.env secret sidecar (permission 0600, does not overwrite existing values on rerun).
  5. Registry Login — Input of docker.kopens.io credentials (automatically skipped if already logged in).
  6. Stack Launch — Network/Volume creation → Image pull → Configuration template seeding → docker compose up -d.

One-time options during installation (specify via environment variables only if necessary):

OptionEffect
SKIP_OS=1Skip OS configuration and Docker installation (if Docker is already installed and running)
SKIP_LOGIN=1Skip registry login (if already logged in or images are local)
SKIP_FW=1Skip firewall configuration (if firewall is managed separately)
DOCKER_DATA_DIR=<path>Change Docker data-root (default /data1/docker-data)
# 예: Docker가 이미 설치된 서버
SKIP_OS=1 sudo -E ./install.sh
Credential replacement must occur «before installation»

The secret sidecar (/etc/kopens/plantpulse-platform.env) uses a mandatory assignment of the form export VAR=값, which overrides shell exports. On a node where installation is complete, PP_PG_PASSWORD=... ./up.sh is quietly ignored. For replacement during delivery, use export PP_*_PASSWORD=... before installation; after installation, use passwd.sh in Password Rotation.

Step 5: Launch and Boot Verification​

install.sh launches the stack and returns. Boot completion must be verified separately.

./up.sh

up.sh is idempotent and safe to run even if the stack is already up, and it waits until the system is ready. Exit code 0 does not mean «the command succeeded» but rather «it is now ready for use».

Environment VariableDefault ValueMeaning
PP_READY_TIMEOUT900Readiness wait upper limit (seconds)
PP_READY_INTERVAL15Verification interval (seconds)
PP_WAIT=0—Do not wait. In this case, 0 does not signify readiness
The first clean installation takes 15-18 minutes

The process launch itself takes 3-5 minutes (JVM warm-up), but it takes 15-18 minutes for all components to stabilize. Aster TSDB schema migration and stabilization are the slowest. Restarts are much faster as the schema already exists.

Measured criteria (2026-08-31, 32 vCPU / 128GiB): Data Lake 217s, Web Server 316s.

You can also run the readiness determination manually:

./stack-verify-boot.sh

This script monitors the entire stack, not just a single container — it verifies if all nine are running/healthy and if the proxy is serving port 443.

Step 6: Status and Operational Inspection​

./status.sh # 0 = 정상 / 2 = 비정상
./ops-check.sh

status.sh summarizes the service list, container status, health, and volumes. The exit code is the contract — it can be used directly in automation.

DeterminationWhat is considered abnormal
Unable to read service listCompose parsing failure or Docker access unavailable
Container missing for a service declared in composeNot started
Container is not runningAll nine must operate continuously — no exceptions
Health of a resident container is unhealthystarting / none are pending
Health API is not OKProbe inside Data Lake

Volumes and configurations are reported only and are not included in the exit code — since it is normal for some to be missing depending on the installation configuration.

ops-check.sh additionally scans for recent critical logs (OOM, FATAL, SSL errors, etc.).

Health Verification​

The monitor API is served by the plantpulse-datalake container. The command differs depending on where you are querying from.

# 컨테이너 안에서 — 어떤 구성에서도 동작하는 방법
docker exec plantpulse-datalake curl -kfsS https://127.0.0.1:4950/api/health | jq

# 호스트/외부에서 — 4950 이 publish 되어 있습니다
curl -kfsS https://<server-ip>:4950/api/health | jq

It is normal if "status" is OK or WARN, and it is a failure if it is FAIL.

Health cannot be viewed on other ports

The console and health API are served on both ports — 4950 (HTTPS) and 4949 (plain HTTP). They are the same console and API, differing only in scheme. 4949 no longer redirects to 4950.

4949 is plain text — login passwords and session cookies flow as is. Use 4950 in untrusted networks. 4949 is an option for boxes where self-signed certificate warnings would actually obstruct the operator.

Installation Deliverables​

Networks and Volumes​

ResourceNamePurpose
Networkpp-netPlatform-dedicated Docker network (default 10.99.0.0/24, gateway 10.99.0.1)
Volumepp-dataPersistent data storage (Aster TSDB, PostgreSQL, Kafka, etc.)
Volumepp-tempTemporary data (Spark, Hive workspace)
Volumepp-backupBackup storage
Volumepp-securityTLS certificates/keystores. Written by plantpulse-certs, read-only for others
Volumepp-proxy-certsCertificates used by proxy on 443

Volumes are always preserved even if containers are stopped or removed, ensuring data is maintained during updates or reinstalls.

Host Directories​

PathPurpose
/opt/kopens/plantpulse-platform-dockerInstallation/operation scripts and master compose file
/etc/kopens/confPlatform configuration templates (host bind mount). Automatically seeded from the image upon first run; subsequently editable by the operator on the host and preserved during reinstallation
/etc/kopens/plantpulse-platform.envService credential secret sidecar (permission 0600)
/etc/kopens/platform.node.envNode identity (values unique to that box, such as IP/disk). Do not copy between nodes
/etc/kopens/caShared cluster CA (used in 2-node split installation)

Access Addresses​

PurposeAddress
Web Consolehttp://<server-ip>/ · https://<server-ip>/ — received by plantpulse-proxy
Admin UIhttps://<server-ip> (same address as web console)
Monitor UI · Healthhttps://<server-ip>:4950/api/health
MQTT<server-ip>:1883 (plain) · <server-ip>:1884 (TLS)
OPC-UA<server-ip>:11004 · <server-ip>:11005

Security Guide: After the first login to the web console, be sure to change the default administrator password. → Initial Password

Operation Command Summary​

Commands used for daily operations. All are executed in /opt/kopens/plantpulse-platform-docker/bin/. All verbs support --help.

TaskCommandNote
Status Inspection./status.shService / health / volume summary. 0=Normal / 2=Abnormal
Operational Inspection./ops-check.shContainer health + Health API + recent critical logs
Launch./up.shIdempotent. 0 = Ready
Stop./down.shPreserve state
Restart./restart.shGraceful drain → Stop → Launch → Wait for readiness
View Logs./logs.sh [서비스]Prints last 200 lines and ends. Use -f to follow. All containers if no argument. List via --list
Enter Container./shell.sh [서비스]Data Lake if no argument
Update StackRerun installation one-linerNot ./update.sh — see Upgrade
Full Removal./remove.shRemove containers only (preserve volumes/configs). RM_IMAGE=1 / RM_NETWORK=1
Backup./backup.sh [볼륨 …]Default pp-data · pp-security
Boot Verification./stack-verify-boot.shFull stack readiness determination
Diagnosis Bundle./doctor.shTarball for support requests. Secret values are masked
Change Password./passwd.sh --listList of changeable keys
# 운영 중 빠른 점검 루틴
cd /opt/kopens/plantpulse-platform-docker/bin
./status.sh
./ops-check.sh

# 문제가 의심되면 진단 번들 생성
./doctor.sh
Old names still work

stack-run.sh · stack-stop.sh · stack-bash.sh · stack-update.sh · stack-remove.sh have not been deleted. Executing them simply provides a one-line guide to the new name, but the operation remains the same. Existing customer runbooks do not need to be updated all at once.

Caution — Total Volume Deletion: ./tools/remove-all-volumes.sh permanently deletes all data. Use only after completing a backup.

Air-gapped Installation​

Air-gapped installation has been moved to Air-gapped Installation. Information on bundle contents, environment variables for airgap-bundle.sh, and what airgap-load.sh blocks before installation is located there.

Troubleshooting​

If preflight inspection fails​

MessageAction
docker is installed but daemon is not ready while SKIP_OS=1Please start the Docker daemon first: systemctl start docker. Or run without SKIP_OS and install.sh will configure Docker
Port already in use (WARN)Another service is using the port. Verify the process with ss -tlnp | grep :<port> and clear it before platform installation
Docker data-root parent path missing (WARN)/data1 directory is missing. Mount the data disk to /data1 or create the directory

If the stack does not start​

docker compose up -d waits for all depends_on conditions; if a single container fails the health check, the entire command fails, leaving only one line.

dependency failed to start: container plantpulse-server-web is unhealthy

This one line tells you nothing other than the container name. The operational script automatically prints the container list, status, health probe output, and log tails at this time, so please read that output first. To check manually:

cd /opt/kopens/plantpulse-platform-docker/bin

# 어떤 컨테이너가 어떤 상태인가
./status.sh

# 문제가 있는 컨테이너의 로그
./logs.sh --list # 볼 수 있는 서비스 목록
./logs.sh plantpulse-server-web -n 200

# 자원 상황
df -h
docker stats --no-stream

Commonly verified causes:

CauseVerification
Out of Memory (OOM)docker inspect <컨테이너> --format '{{.State.OOMKilled}}'. Refer to Environment Variable Reference for app-specific mem_limit
Disk FullFree space via /data1
Missing Required CredentialsCompose requires passwords as :?. If empty, the stack will not even partially start — run install.sh first to create the sidecar
DB Initialization DelayFirst installations take time due to schema creation. If proceeding without errors in the logs, please wait longer

If the cause is difficult to identify, create a diagnosis bundle using ./doctor.sh and attach it to your technical support request. The diagnosis bundle contains the status and logs of all containers.

If TLS handshake only appears as TimeoutException​

If the name used by an app to connect to the backend is not in the certificate SAN list, the error message will not mention certificates at all and will look like a timeout. If you have changed container names or manually specified a backend host, suspect this case. The method to rebake certificates is in Security Management.

Registry Authentication Failure​

# unauthorized 오류 시 재로그인
docker login docker.kopens.io
  • If login credentials are missing or expired, request issuance from the KOPENS operations team.
  • Internal firewalls may block registry access. Request HTTPS access allowance for docker.kopens.io and product.kopens.io domains from your network administrator.
  • For environments where external access is fundamentally blocked, please use Air-gapped Installation.

Technical Support​

If you need assistance during installation or operation, please contact us at any time: webmaster@kopens.com