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 Source | What is here | Who reads it |
|---|---|---|
plantpulse-platform-docker/bin/env.sh | Values determined on the host — credential defaults, docker runtime, network, resource values calculated on the host | Host operation scripts |
plantpulse-platform-docker/compose/docker-compose.yml | Values actually seen by the container | Container |
env.shThe 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.
| Tier | What determines the value |
|---|---|
plantpulse-datalake | Value passed by compose → pd of the container receives it as ${VAR:-기본값}. The passed value wins |
| Six Apps | Environment variables passed by compose override the internal defaults of the jar/WAR |
plantpulse-certs | Only 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.
| Priority | Source | Reason 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 |
| 3 | export of the calling shell | Overrides the :- of the default value — only if 1 and 2 did not specify that name |
| 4 (Lowest) | ${VAR:-기본값} of bin/env.sh | Only when unset |
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/
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.
| File | Status |
|---|---|
/etc/kopens/plantpulse-platform.env | Current Master — Common protocol for platform · ai · studio products (plantpulse-<product>.env), authority 0600 |
/opt/kopens/plantpulse-platform.env | Old 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.
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.
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 |
DOCKER_PP_EXTERNAL_IPThis 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
| Variable | Default Value | Description |
|---|---|---|
DOCKER_PP_NETWORK | pp-net | docker network name |
DOCKER_SUBNET | 10.99.0.0/24 | Subnet (to avoid overlap with other container networks) |
DOCKER_GATEWAY | 10.99.0.1 | gateway |
DOCKER_PP_IP | 10.99.0.100 | Fixed 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.
| Variable | Default Value | Service |
|---|---|---|
DOCKER_SERVER_MEMORY / DOCKER_SERVER_HEAP | 14g / 8g | plantpulse-server-web |
DOCKER_BATCH_MEMORY / DOCKER_BATCH_HEAP | 4g / 3g | plantpulse-batch-web |
DOCKER_WAREHOUSE_MEMORY | 8g | plantpulse-warehouse-web |
DOCKER_OPCUA_MEMORY | 3g | plantpulse-plugin-opcua |
DOCKER_AASX_MEMORY | 3g | plantpulse-plugin-aasx |
DOCKER_HA_MEMORY | 2g | plantpulse-ha |
DOCKER_DATALAKE_MEMORY | 96g | plantpulse-datalake |
Volumes
| Variable | Default Value | Purpose |
|---|---|---|
DOCKER_PP_DATA_VOLUME | pp-data | Permanent data storage |
DOCKER_PP_TEMP_VOLUME | pp-temp | Temporary data |
DOCKER_PP_BACKUP_VOLUME | pp-backup | Backup storage |
DOCKER_PP_SECURITY_VOLUME | pp-security | TLS 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
| Variable | Effect |
|---|---|
SKIP_OS=1 | Skip OS setup |
SKIP_LOGIN=1 | Skip registry login |
SKIP_FW=1 | Skip firewall configuration |
DOCKER_DATA_DIR | Docker 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.
| What | Where | Note |
|---|---|---|
🔑 Various *_PASSWORD · API keys | bin/passwd.sh / bin/rotate-secret.sh | Must be replaced during commissioning. Not changed via file editing |
PP_LANG | bin/env.sh | For Korean operation, use ko (Default en) |
PP_TZ | bin/env.sh | For Korean operation, maintain Asia/Seoul |
DOCKER_PP_EXTERNAL_IP | bin/env.sh | Only when behind NAT. Otherwise, keep as empty value |
DOCKER_DATALAKE_MEMORY | bin/env.sh | Default 96G. Automatically lowered if the host is small |
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
| Variable | Default Value | Description |
|---|---|---|
PP_HOME | /opt/kopens/plantpulse-platform | Installation root (identical inside container) |
PP_SCHEME | PP | Deployment identification scheme |
PP_MODE | MASTER | Node role (MASTER / Worker) — Clustering (horizontal expansion) axis |
PP_OPTIONS | {} | Dedicated JSON for individual service on/off (see §3.1 below) |
PP_HOST_IP | 127.0.0.1 | IP of this node |
PP_SERVICE_IP | 127.0.0.1 | Service bind IP |
PP_MASTER_IP | 127.0.0.1 | Master (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_IP | 127.0.0.1 | External public IP (NAT environment) |
PP_DATA_DIR | /data1/pp-data | Data root |
PP_TEMP_DIR | /data1/pp-temp | Temporary file root |
PP_BACKUP_DIR | /data1/pp-backup | Backup root |
PP_DATA_DISK_NAME | sdb | Data disk device name |
PP_CLUSTER_CORES | 30 | Number of cores used |
PP_CLUSTER_MEMORY_BY_CORE | 2G | Memory allocation per core |
PP_LANG | en | Default language |
PP_TZ | Asia/Seoul | Timezone (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
| Variable | Default Value | Description |
|---|---|---|
PP_REDIS_HOST / PP_REDIS_PORT | plantpulse-datalake / 6379 | Valkey/Redis (TLS listener 6380) |
PP_REDIS_USER / 🔑 PP_REDIS_PASSWORD | redis / — | Redis account |
PP_POSTGRES_HOST / PP_POSTGRES_PORT | plantpulse-datalake / 5432 | PostgreSQL 18 (Percona) |
PP_PG_USER / 🔑 PP_PG_PASSWORD | plantpulse / — | PostgreSQL account |
PP_CASSANDRA_HOST / PP_CASSANDRA_PORT | plantpulse-datalake / 9042 | Aster TSDB |
PP_CASSANDRA_USER / 🔑 PP_CASSANDRA_PASSWORD | cassandra / — | Aster TSDB account |
PP_CASSANDRA_PORT | 9042 | Aster TSDB port reused by storage layer clients. A variable named PP_STORAGE_PORT does not exist |
PP_S3_HOST / PP_S3_PORT | plantpulse-datalake / 9000 | RustFS object storage |
PP_S3_USER / 🔑 PP_S3_PASSWORD | rustfs / — | RustFS account |
PP_S3_BUCKET | plantpulse | Default bucket — Iceberg data · backups |
PP_S3_UPLOAD_BUCKET | upload | Upload 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_NAME | pp | PostgreSQL database name |
PP_KEYSPACE | pp | Aster TSDB keyspace name |
PP_HIVE_DB | hive-metastore-230 | Hive metastore DB |
5. Messaging
| Variable | Default Value | Description |
|---|---|---|
PP_MQ_USER / 🔑 PP_MQ_PASSWORD | mq / — | Kafka·MQTT shared account (no dedicated Kafka account) |
PP_TOPIC_PREFIX | pp | Kafka topic prefix (pp-tag-point, etc.) |
PP_KAFKA_HOST / PP_KAFKA_PORT | PP_MASTER_IP / 9092 | Kafka broker |
PP_KAFKA_TLS_PORT | 9094 | Kafka external TLS |
PP_KAFKA_CONTROLLER_PORT | 9093 | Kafka KRaft controller |
PP_MQTT_HOST / PP_MQTT_PORT | PP_MASTER_IP / 1883 | MQTT (HiveMQ) |
PP_MQTT_TLS_PORT | 1884 | MQTT TLS |
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
| Variable | Default Value | Description |
|---|---|---|
PP_CEP_HOST / PP_CEP_PORT / PP_CEP_TLS_PORT | plantpulse-datalake / 7400 / 7401 | CEP server |
PP_CEP_PROTOCOL | http | CEP 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_PORT | plantpulse-datalake / 5500 / 5501 | Data Gateway |
PP_DATA_GATEWAY_PROTOCOL | http | DGW 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_PORT | plantpulse-datalake / 7800 / 7801 | Time Series Engine (TSE). Server listens on both http 7800 and https 7801 |
PP_TSE_PROTOCOL | http | TSE 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_PASSWORD | tse / — | TSE account |
PP_ANALYTICS_USER / 🔑 PP_ANALYTICS_PASSWORD | spark / — | Analysis (Spark/Kyuubi client) account. Separate credential from Kyuubi internal metastore (PP_HIVE_*) |
PP_HIVE_HOST / PP_HIVE_PORT | PP_MASTER_IP / 9083 | Hive metastore |
PP_HIVE_USER / 🔑 PP_HIVE_PASSWORD | hive / — | Hive account |
PP_SPARK_HOST / PP_SPARK_PORT | PP_MASTER_IP / 7077 | Spark master |
PP_KYUUBI_PORT | 10000 | Kyuubi SQL gateway |
PP_GRAVITINO_USER / 🔑 PP_GRAVITINO_PASSWORD | gravitino / — | Gravitino catalog |
PP_TEMPORAL_HOST / PP_TEMPORAL_PORT | plantpulse-datalake / 7233 | Temporal engine (Web UI 8233) |
PP_TEMPORAL_USER / 🔑 PP_TEMPORAL_PASSWORD | temporal / — | Temporal account |
PP_KESTRA_HOST / PP_KESTRA_PORT | plantpulse-datalake / 8380 | Kestra scheduler |
PP_KESTRA_ADMIN_EMAIL / 🔑 PP_KESTRA_ADMIN_PASSWORD | admin@plantpulse.io / — | Kestra administrator |
PP_KESTRA_DB_USER / 🔑 PP_KESTRA_DB_PASSWORD | kestra / — | Kestra meta DB |
7. Plugins · Observability · Web Layer
| Variable | Default Value | Description |
|---|---|---|
PP_OPCUA_TCP_PORT | 11004 | OPC-UA opc.tcp protocol port |
PP_OPCUA_TLS_PORT | 11005 | OPC-UA TLS |
PP_AAS_SERVER_PORT | 8090 | AAS(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_PASSWORD | grafana / — | PlantPulse Dashboard repository (PostgreSQL) connection. If empty, pd start exits with 3. There is no variable to determine the administrator account |
The following cannot be changed via env — even if values are specified, nothing will happen.
| What | Port | Why |
|---|---|---|
| PlantPulse Dashboard | 3000 | Hardcoded 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/AAS | 4800 · 4801 | Hardcoded 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
| Variable | Default Value | Description |
|---|---|---|
PP_TLS_ENABLED | true | TLS Master Switch — Determines the default protocol for service clients |
PP_TLS_CERT_DIR | /var/security/plantpulse | Certificate directory |
PP_TLS_DOMAIN | plantpulse.io | Certificate domain |
🔑 PP_TLS_KEYSTORE_PASSWORD / 🔑 PP_TLS_TRUSTSTORE_PASSWORD | — | keystore/truststore password (must be identical across all nodes) |
PP_TLS_VALID_DAYS | 3650 | Certificate validity period (days) |
PP_TLS_SAN_IPS | PP_HOST_IP,…,127.0.0.1 | Certificate SAN IP. Must include external access IP (e.g., public IP) in production |
PP_TLS_SAN_DNS | localhost,<hostname>,<domain> | Certificate SAN DNS |
PP_TLS_NODE_NAMES | master worker-1 … worker-5 | Cluster node name |
PP_TLS_FORCE_REGENERATE | false | Force certificate regeneration during true |
9. API Keys / AI Gateway
All have development defaults (fallbacks). Must be replaced with Docker
-eorexportduring production/customer delivery.
| Variable | Description |
|---|---|
🔑 PP_API_KEY | Internal platform API key (Server ↔ Edge Agent authentication) |
🔑 PP_DATA_GATEWAY_API_KEY | server-web → data-gateway JDBC authentication |
🔑 PP_CEP_API_KEY | server-web → CEP server X-API-Key (must match CEP server cep.api.key) |
🔑 PP_FLOW_WEBHOOK_API_KEY | Flow Engine external webhook authentication |
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 = 쓸 수 있다)
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:
| Check | Detail |
|---|---|
| ① 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.