Skip to main content

Environment Variable Reference

All settings for PlantPulse originate from a single set of environment variables. This page serves as a reference, organizing the variables encountered during installation and operation by category.

This page covers infrastructure/deployment environment variables (PP_* · DOCKER_PP_*). For application behavior properties (engine.*, websocket.*, etc.), please refer to the Property Reference.

1. Where are the configuration files?​

The Platform operates as a Docker Compose stack, and values are defined in two locations.

Definition SourceWhat is hereWho reads it
plantpulse-platform-docker/bin/env.shValues determined on the host — credential defaults, docker runtime, network, resource values calculated on the hostHost operation scripts
plantpulse-platform-docker/compose/docker-compose.ymlValues actually seen by the containerContainer
App containers do not read env.sh

The six apps (plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse-web · plantpulse-plugin-opcua · plantpulse-plugin-aasx · plantpulse-ha) do not source env.sh. The only definition source the apps see is the compose file.

In other words, a name that exists only in env.sh and not in the compose file will not reach the app, and the app will use the internal defaults embedded in the jar/WAR. In fact, there was an incident on 2026-08-29 where five apps continued to use old names at a site where identifiers were changed only in the datalake.

The way values are determined differs by tier.

TierWhat determines the value
plantpulse-datalakeValue passed by compose → pd of the container receives it as ${VAR:-기본값}. The passed value wins
Six AppsEnvironment variables passed by compose override the internal defaults of the jar/WAR
plantpulse-certsOnly looks at PP_TLS_* of the compose file. Does not source files

Priority — What overrides what​

The order in which bin/env.sh runs from top to bottom on the host side is the priority.

PrioritySourceReason for Winning
1 (Highest)/etc/kopens/plantpulse-platform.env (Secret Sidecar)Because export VAR=값 is an unconditional assignment, it overrides shell exports
2/etc/kopens/platform.node.env (Per Node)Sourced after the sidecar; also an unconditional assignment
3export of the calling shellOverrides the :- of the default value — only if 1 and 2 did not specify that name
4 (Lowest)${VAR:-기본값} of bin/env.shOnly when unset
The most common mistake is forgetting that priority 1 is the highest

On a node where installation is complete, PP_PG_PASSWORD=새값 bin/up.sh is silently ignored. This is because the sidecar already holds that name. The only way to change the value is via bin/passwd.sh / bin/rotate-secret.sh.

🔑 The master copy of secret values is under /etc/kopens/​

DB · messaging passwords and API keys are in the sidecar, not the two files above

The actual values of service account passwords (PP_PG_PASSWORD · PP_CASSANDRA_PASSWORD · PP_MQ_PASSWORD, etc.) and API keys are located in sidecar files outside the repo tree.

FileStatus
/etc/kopens/plantpulse-platform.envCurrent Master — Common protocol for platform · ai · studio products (plantpulse-<product>.env), authority 0600
/opt/kopens/plantpulse-platform.envOld Path (existed only from 2026-08-25 to 08-29) — Neither read nor written. If it remains, the installation script reverts it to the master copy

First, check if the master copy exists using ls -l /etc/kopens/.

The sidecar overrides bin/env.sh. This is because env.sh sources the sidecar first at the top, and all subsequent values are in the form of ${VAR:-기본값}. Therefore, simply changing the password in env.sh will result in nothing happening — the old value in the sidecar will continue to win.

Random numbers are not generated during installation

install.sh loads the defaults of bin/env.sh directly into the sidecar. Therefore, they must be replaced during commissioning → Initial Passwords

Since compose requires passwords as ${PP_X:?}, if a value is empty, the stack will not start at all rather than booting in a «half-configured» state.

Do not change passwords or API keys via file editing

Fixing the sidecar alone does not complete the reflection. For values where the server account is the master, such as PostgreSQL · Aster TSDB, the server side must also be changed. If the sequence is wrong, the platform will not start.

Use the dedicated procedure — Change Password (Credential Rotation). This is a task to be performed once during commissioning.

🔑 indicates a secret value. Do not put raw text in git, issues, or chats.

1.5 Container Runtime · Host Resources (DOCKER_*)​

Values determined by bin/env.sh. The resource values below are calculated based on the host, not fixed defaults — using constants may cause the host to reject the startup (if --cpus is larger than the actual cores, docker rejects it) or cause an OOM-kill during boot. Values specified per box always win.

| Variable | How it is determined | |---|---|---| | DOCKER_CMD | docker (podman also possible) | | DOCKER_REGISTRY | docker.kopens.io/pp — Registry prefix for the eight stack images | | PP_IMAGE_TAG | latest. Fixed tags like 2026.08 are recommended for releases | | DOCKER_PP_CPUS | nproc (falls back to 8 on read failure) | | DOCKER_PP_CLUSTER_CORES | DOCKER_PP_CPUS - 2, minimum 4 · maximum 30. Passed to container as PP_CLUSTER_CORES | | DOCKER_PP_MEMORY | 90% of host RAM, minimum 8G | | DOCKER_DATALAKE_MEMORY | 96G — Minimum for datalake master. It is capped so it does not exceed 90% of host RAM; if the host is less than 107G, 96G cannot be provided and the host is underspecified. 64G is not a supported specification (System Requirements) | | DOCKER_PW_MEMORY | 60G, 90% of RAM if the host is small — Workers use the same image as masters but run fewer services, so they are set separately | | DOCKER_PP_DATA_DISK_NAME | Traces actual disk supporting / via findmnt + lsblk (LVM/dm down to physical disk). Falls back to sda on failure | | DOCKER_PP_EXTERNAL_IP | Default empty value. Actual public IP only in NAT environments |

Do not enter an invalid IP in DOCKER_PP_EXTERNAL_IP

This value passes through PP_SERVICE_IP and enters the SAN of the TLS certificate. If even one incorrectly formatted IP is mixed in, openssl will reject the entire extension file and not a single certificate will be generated.

Network​

VariableDefault ValueDescription
DOCKER_PP_NETWORKpp-netdocker network name
DOCKER_SUBNET10.99.0.0/24Subnet (to avoid overlap with other container networks)
DOCKER_GATEWAY10.99.0.1gateway
DOCKER_PP_IP10.99.0.100Fixed IP for datalake

App-specific Memory Limits — determined by compose​

Setting mem_limit separately for each app is the primary purpose of container separation (OOM isolation). Even if one app reaches its limit, other apps and infrastructure remain alive.

VariableDefault ValueService
DOCKER_SERVER_MEMORY / DOCKER_SERVER_HEAP14g / 8gplantpulse-server-web
DOCKER_BATCH_MEMORY / DOCKER_BATCH_HEAP4g / 3gplantpulse-batch-web
DOCKER_WAREHOUSE_MEMORY8gplantpulse-warehouse-web
DOCKER_OPCUA_MEMORY3gplantpulse-plugin-opcua
DOCKER_AASX_MEMORY3gplantpulse-plugin-aasx
DOCKER_HA_MEMORY2gplantpulse-ha
DOCKER_DATALAKE_MEMORY96gplantpulse-datalake

Volumes​

VariableDefault ValuePurpose
DOCKER_PP_DATA_VOLUMEpp-dataPermanent data storage
DOCKER_PP_TEMP_VOLUMEpp-tempTemporary data
DOCKER_PP_BACKUP_VOLUMEpp-backupBackup storage
DOCKER_PP_SECURITY_VOLUMEpp-securityTLS materials
DOCKER_PP_PROXY_CERTS_VOLUME—Non-existent variable. Proxy certificate volume pp-proxy-certs is hardcoded by name in compose

install.sh One-time Options​

VariableEffect
SKIP_OS=1Skip OS setup
SKIP_LOGIN=1Skip registry login
SKIP_FW=1Skip firewall configuration
DOCKER_DATA_DIRDocker data-root path (Default /data1/docker-data)

2. Minimum Checklist​

For a new single-server installation, there are only a few things to actually touch, as resource values are calculated by the installation process based on the host.

WhatWhereNote
🔑 Various *_PASSWORD · API keysbin/passwd.sh / bin/rotate-secret.shMust be replaced during commissioning. Not changed via file editing
PP_LANGbin/env.shFor Korean operation, use ko (Default en)
PP_TZbin/env.shFor Korean operation, maintain Asia/Seoul
DOCKER_PP_EXTERNAL_IPbin/env.shOnly when behind NAT. Otherwise, keep as empty value
DOCKER_DATALAKE_MEMORYbin/env.shDefault 96G. Automatically lowered if the host is small
Variables in PP_* from §3 onwards are «names seen by the container»

Values such as PP_HOST_IP · PP_DATA_DIR · PP_CLUSTER_CORES are names used inside the container, and the entities that determine those values on the host are compose and DOCKER_* of bin/env.sh (§1.5). For example, the number of cores is calculated by DOCKER_PP_CLUSTER_CORES and passed to the container as PP_CLUSTER_CORES.

Do not expect a change to be reflected just by modifying PP_CLUSTER_CORES on the host — if it is not passed, Kyuubi will never start, trusting a 30-core setting that does not exist on a 16 vCPU box.

3. Platform Defaults / Network / Resources​

VariableDefault ValueDescription
PP_HOME/opt/kopens/plantpulse-platformInstallation root (identical inside container)
PP_SCHEMEPPDeployment identification scheme
PP_MODEMASTERNode role (MASTER / Worker) — Clustering (horizontal expansion) axis
PP_OPTIONS{}Dedicated JSON for individual service on/off (see §3.1 below)
PP_HOST_IP127.0.0.1IP of this node
PP_SERVICE_IP127.0.0.1Service bind IP
PP_MASTER_IP127.0.0.1Master (datalake) IP viewed by Worker/APP tiers. Also the source for Kafka advertised.listeners. If not specified during DATALAKE tier installation, automatically detected as host IP
PP_PUBLIC_IP127.0.0.1External public IP (NAT environment)
PP_DATA_DIR/data1/pp-dataData root
PP_TEMP_DIR/data1/pp-tempTemporary file root
PP_BACKUP_DIR/data1/pp-backupBackup root
PP_DATA_DISK_NAMEsdbData disk device name
PP_CLUSTER_CORES30Number of cores used
PP_CLUSTER_MEMORY_BY_CORE2GMemory allocation per core
PP_LANGenDefault language
PP_TZAsia/SeoulTimezone (automatically applied to all java processes)

3.1 PP_OPTIONS — Individual Service on/off​

PP_OPTIONS is a JSON for turning individual components on and off. The default is {}, and any unspecified keys are considered true (active).

Example: To turn off the internal Valkey by using an externally managed Redis:

export PP_OPTIONS='{"enable-valkey":false}'

Available enable-* keys (all default true):

enable-messaging · enable-valkey · enable-postgres · enable-cassandra · enable-spark · enable-rustfs · enable-tse · enable-cep · enable-workflow · enable-data-gateway · enable-sql · enable-hive · enable-gravitino · enable-kyuubi · enable-grafana · enable-monitor

4. Datastore​

VariableDefault ValueDescription
PP_REDIS_HOST / PP_REDIS_PORTplantpulse-datalake / 6379Valkey/Redis (TLS listener 6380)
PP_REDIS_USER / 🔑 PP_REDIS_PASSWORDredis / —Redis account
PP_POSTGRES_HOST / PP_POSTGRES_PORTplantpulse-datalake / 5432PostgreSQL 18 (Percona)
PP_PG_USER / 🔑 PP_PG_PASSWORDplantpulse / —PostgreSQL account
PP_CASSANDRA_HOST / PP_CASSANDRA_PORTplantpulse-datalake / 9042Aster TSDB
PP_CASSANDRA_USER / 🔑 PP_CASSANDRA_PASSWORDcassandra / —Aster TSDB account
PP_CASSANDRA_PORT9042Aster TSDB port reused by storage layer clients. A variable named PP_STORAGE_PORT does not exist
PP_S3_HOST / PP_S3_PORTplantpulse-datalake / 9000RustFS object storage
PP_S3_USER / 🔑 PP_S3_PASSWORDrustfs / —RustFS account
PP_S3_BUCKETplantpulseDefault bucket — Iceberg data · backups
PP_S3_UPLOAD_BUCKETuploadUpload bucket. Loaded only in plantpulse-server-web (intentionally not sent to workers). Buckets are separated so that retention rules on one side do not affect the other
PP_DB_NAMEppPostgreSQL database name
PP_KEYSPACEppAster TSDB keyspace name
PP_HIVE_DBhive-metastore-230Hive metastore DB

5. Messaging​

VariableDefault ValueDescription
PP_MQ_USER / 🔑 PP_MQ_PASSWORDmq / —Kafka·MQTT shared account (no dedicated Kafka account)
PP_TOPIC_PREFIXppKafka topic prefix (pp-tag-point, etc.)
PP_KAFKA_HOST / PP_KAFKA_PORTPP_MASTER_IP / 9092Kafka broker
PP_KAFKA_TLS_PORT9094Kafka external TLS
PP_KAFKA_CONTROLLER_PORT9093Kafka KRaft controller
PP_MQTT_HOST / PP_MQTT_PORTPP_MASTER_IP / 1883MQTT (HiveMQ)
PP_MQTT_TLS_PORT1884MQTT TLS
STOMP(ActiveMQ) has been retired

As browser real-time push changed to SSE, the STOMP broker was removed from the datalake image. The PP_STOMP_* variable and ports 61000/61004 are no longer in service. Please clean them up if they remain in old firewall rules.

6. Processing · Time Series · Analysis · Workflow​

VariableDefault ValueDescription
PP_CEP_HOST / PP_CEP_PORT / PP_CEP_TLS_PORTplantpulse-datalake / 7400 / 7401CEP server
PP_CEP_PROTOCOLhttpCEP connection protocol. https is recommended for distributed/container setups
PP_CEP_CONNECT_PORT(Derived)server-web → CEP connection port. Automatically aligned by protocol (http → 7400, https → 7401). Explicit values take priority
PP_DATA_GATEWAY_HOST / PP_DATA_GATEWAY_PORT / PP_DATA_GATEWAY_TLS_PORTplantpulse-datalake / 5500 / 5501Data Gateway
PP_DATA_GATEWAY_PROTOCOLhttpDGW connection protocol (internal loopback defaults to http)
PP_DATA_GATEWAY_CONNECT_PORT(Derived)DGW connection port. Automatically aligned by protocol (http → 5500, https → 5501). Explicit values take priority
PP_TSE_HOST / PP_TSE_PORT / PP_TSE_TLS_PORTplantpulse-datalake / 7800 / 7801Time Series Engine (TSE). Server listens on both http 7800 and https 7801
PP_TSE_PROTOCOLhttpTSE client connection protocol. Defaults to plaintext http for internal communication — separated from TLS master switch (PP_TLS_ENABLED) (2026-08)
PP_TSE_CONNECT_PORT(Unset)Operator explicit override hook not exported by env.sh. Templates consume ${PP_TSE_CONNECT_PORT:7800} as fallback
PP_TSE_JDBC_SCHEME—Non-existent variable. The jdbc.tse.url template hardcodes ts, so the scheme cannot be changed
PP_TSE_USER / 🔑 PP_TSE_PASSWORDtse / —TSE account
PP_ANALYTICS_USER / 🔑 PP_ANALYTICS_PASSWORDspark / —Analysis (Spark/Kyuubi client) account. Separate credential from Kyuubi internal metastore (PP_HIVE_*)
PP_HIVE_HOST / PP_HIVE_PORTPP_MASTER_IP / 9083Hive metastore
PP_HIVE_USER / 🔑 PP_HIVE_PASSWORDhive / —Hive account
PP_SPARK_HOST / PP_SPARK_PORTPP_MASTER_IP / 7077Spark master
PP_KYUUBI_PORT10000Kyuubi SQL gateway
PP_GRAVITINO_USER / 🔑 PP_GRAVITINO_PASSWORDgravitino / —Gravitino catalog
PP_TEMPORAL_HOST / PP_TEMPORAL_PORTplantpulse-datalake / 7233Temporal engine (Web UI 8233)
PP_TEMPORAL_USER / 🔑 PP_TEMPORAL_PASSWORDtemporal / —Temporal account
PP_KESTRA_HOST / PP_KESTRA_PORTplantpulse-datalake / 8380Kestra scheduler
PP_KESTRA_ADMIN_EMAIL / 🔑 PP_KESTRA_ADMIN_PASSWORDadmin@plantpulse.io / —Kestra administrator
PP_KESTRA_DB_USER / 🔑 PP_KESTRA_DB_PASSWORDkestra / —Kestra meta DB

7. Plugins · Observability · Web Layer​

VariableDefault ValueDescription
PP_OPCUA_TCP_PORT11004OPC-UA opc.tcp protocol port
PP_OPCUA_TLS_PORT11005OPC-UA TLS
PP_AAS_SERVER_PORT8090AAS(BaSyx V3) API. UI is 8091, Registry/AAS ports (4800/4801) are hardcoded in the code and cannot be changed via env
PP_GRAFANA_DB_USER / 🔑 PP_GRAFANA_DB_PASSWORDgrafana / —PlantPulse Dashboard repository (PostgreSQL) connection. If empty, pd start exits with 3. There is no variable to determine the administrator account
Some variables for changing ports «do not exist»

The following cannot be changed via env — even if values are specified, nothing will happen.

WhatPortWhy
PlantPulse Dashboard3000Hardcoded as a number in ports: of compose (PP_GRAFANA_PORT does not exist)
WebSocket push—PP_WEBSOCKET_PORT · PP_WEBSOCKET_PORT_SSL · PP_WEBSOCKET_USER · PP_WEBSOCKET_SERVER_PASSWORD do not exist anywhere in the repository
AAS Registry/AAS4800 · 4801Hardcoded in code

PP_OPCUA_PORT also does not exist — OPC-UA has only PP_OPCUA_TCP_PORT.

Mail server and console · field app addresses are not environment variables. PP_MAIL_SMTP_* is not read even if specified; values are entered in pp.mm_config — Mail Configuration · Address Configuration.

8. TLS / SSL​

VariableDefault ValueDescription
PP_TLS_ENABLEDtrueTLS Master Switch — Determines the default protocol for service clients
PP_TLS_CERT_DIR/var/security/plantpulseCertificate directory
PP_TLS_DOMAINplantpulse.ioCertificate domain
🔑 PP_TLS_KEYSTORE_PASSWORD / 🔑 PP_TLS_TRUSTSTORE_PASSWORD—keystore/truststore password (must be identical across all nodes)
PP_TLS_VALID_DAYS3650Certificate validity period (days)
PP_TLS_SAN_IPSPP_HOST_IP,…,127.0.0.1Certificate SAN IP. Must include external access IP (e.g., public IP) in production
PP_TLS_SAN_DNSlocalhost,<hostname>,<domain>Certificate SAN DNS
PP_TLS_NODE_NAMESmaster worker-1 … worker-5Cluster node name
PP_TLS_FORCE_REGENERATEfalseForce certificate regeneration during true

9. API Keys / AI Gateway​

All have development defaults (fallbacks). Must be replaced with Docker -e or export during production/customer delivery.

VariableDescription
🔑 PP_API_KEYInternal platform API key (Server ↔ Edge Agent authentication)
🔑 PP_DATA_GATEWAY_API_KEYserver-web → data-gateway JDBC authentication
🔑 PP_CEP_API_KEYserver-web → CEP server X-API-Key (must match CEP server cep.api.key)
🔑 PP_FLOW_WEBHOOK_API_KEYFlow Engine external webhook authentication
🔴 AI is no longer configured via environment variables

PP_AI_OPENAI_ENABLED · PP_AI_OPENAI_BASE_URL · PP_AI_OPENAI_API_KEY · PP_AI_OPENAI_MODEL do nothing as of 2026-09-18. They are silently ignored without errors or warnings, and the server appears to be configured.

The master copy of AI settings is the PostgreSQL pp.mm_config table, and the key name has changed from ai.openai.* to ai.llm.*. The way to enter values is through the console settings screen (/env) or the table — Property Reference — AI Settings.

10. Application Procedure After Change​

vi /etc/kopens/plantpulse-platform.env # 사이드카 — 여기를 고칩니다
cd /opt/kopens/plantpulse-platform-docker/bin
./restart.sh # 준비될 때까지 대기 (종료 코드 0 = 쓸 수 있다)
Do not modify inside the installation tree

Values written in bin/env.sh disappear during re-installation or upgrades. This is because the installation script deletes the entire installation tree and downloads it again. The sidecar survives because it is outside that tree.

If you need to change the value seen by the container, modify compose/docker-compose.yml and restart using the same method. Configuration templates are bind-mounted to the host's /etc/kopens/conf, so they can be edited directly on the host and are preserved even after re-installation.

If a variable change is not reflected, please check in the following order:

CheckDetail
① Is the sidecar winning?If the same name exists in /etc/kopens/plantpulse-platform.env, both env.sh and shell exports lose → Priority
② Is the name reaching the app?The six apps do not read env.sh. If it is not in compose, it does not reach the app
③ Did you restart?Most values, including locale and timezone, are reflected only at boot time
④ Is it a password?Passwords are not changed via file editing → Password Rotation

For detailed procedures, please refer to the Startup Guide.