Environment Variable Configuration
Passwords appearing in this document (such as CERT_PASS) are defaults for internal dev boxes and are not secret.
Do not use them as-is in customer or mass production deployments:
install.shgenerates a random 14-character string ifCERT_PASSis not specified (bin/install.sh:298). The generated value is recorded in/etc/kopens/credentials.txt(0600, root) and the factory label.- The box OS access password is also changed during installation.
- Therefore, the default values in this document are only valid for a single internal dev box.
PlantPulse Edge configuration is divided into two layers.
| Layer | What | Where |
|---|---|---|
| Environment Variables (env) ← This page | Language/timezone, JDK path, TLS password, data path, site values during installation | env.sh / /etc/kopens/*.env / systemd / docker -e |
| Application Settings (properties) | edge.id / cassandra.* / mqtt.* / OPC-UA runtime keys, etc. | app.properties → app.properties Reference |
Once the env layer determines the language, timezone, JVM options, passwords, and configuration directory locations,
app.properties operates on top of it. These are values that are set once per box and rarely change.
Value Determination Order
env.sh is the single source that all installation, upgrade, and startup scripts source.
If the same variable exists in multiple locations, the first one defined wins — because the shell's ${VAR:-기본값} only triggers «when the variable is missing».
| Order | Where | What |
|---|---|---|
| 1 | /etc/kopens/env.sh.generated | Sidecar created once by install.sh. API_KEY · ADMIN_PASS · MQTT_PASS · OPCUA_PASS · CERT_PASS. 0600 root |
| 2 | ${VAR:-기본값} within env.sh | Default values declared in the file |
| 3 | Operator export lines at the end of env.sh | The last one written wins |
Secrets created during installation are preserved even if you run install.sh again (because step 1 is determined first).
If an operator wants to force a value, they can add a line of export at the end of env.sh.
If you run install.sh again after deleting /etc/kopens/env.sh.generated, passwords and API keys are regenerated. This will cause a mismatch with the API keys registered on the upper Platform, stopping collection — check /etc/kopens/credentials.txt before deleting.
1. Core env Variables at a Glance
| Variable | Meaning | Default |
|---|---|---|
PP_LANG | UI/OS/JVM Language (BCP 47) — Single language per box | en |
PP_TZ | Timezone (java.util.TimeZone ID) | Asia/Seoul |
JAVA_HOME | JDK Path (class file 65 → JDK 21) | /usr/lib/jvm/java-21-openjdk |
PE_HOME | Gateway Root | /opt/kopens/plantpulse-edge |
PE_DATA_DIR | Data Directory (Cassandra/Redis/HiveMQ/Node-RED) | /data1 |
CERT_PASS | TLS keystore password (Common for Tomcat/HiveMQ/OPC-UA) | Randomly generated during installation |
CLEAN_ON_STARTUP | Whether to clear work/temp upon booting | false |
JAVA_TOOL_OPTIONS | Common options for all JVMs (entrypoint/setenv.sh injects language/timezone) | (Auto-configured) |
CERT_PASS / SERVER_API_KEY / various passwords must not be exposed in shell history, ps argv, or logs.
Run installation after injecting via *_FILE (e.g., EDGE_ADMIN_PASSWORD_HASH_FILE, CASSANDRA_PASSWORD_FILE) or sourcing a root 0600 file using source. Random credentials generated by install.sh are stored once in /etc/kopens/credentials.txt (chmod 0600 root).
2. Language / Timezone — PP_LANG / PP_TZ
PlantPulse enforces a single language/timezone per box. User-specific cookies / Accept-Language are ignored, and the webapp + host OS + container OS + JVM all use the same value.
PP_LANG=en PP_TZ=Asia/Seoul # 글로벌 default (영문 UI + 한국 시간)
PP_LANG=ko PP_TZ=Asia/Seoul # 완전 한국 박스
PP_LANG=en PP_TZ=UTC # 완전 영문 박스
2.1 Propagation Chain (4 layer)
env.sh / i18n.env ─→ install.sh ─→ systemd EnvironmentFile + docker -e ─→ container-entrypoint.sh ─→ webapp PpFixedLocaleResolver
(값 정의) (host locale) (PP_LANG / PP_TZ 주입) (OS LANG/TZ + JAVA_TOOL_OPTIONS) (UI 언어 결정)
2.2 What the entrypoint does in the container
container-entrypoint.sh maps PP_LANG to POSIX locale and configures JVM options:
PP_LANG="${PP_LANG:-en}"; PP_TZ="${PP_TZ:-Asia/Seoul}"
case "$PP_LANG" in
ko|ko_*|ko-*) LANG=ko_KR.UTF-8 ; lang=ko country=KR ;;
en|en_*|en-*) LANG=en_US.UTF-8 ; lang=en country=US ;;
esac
export LANG TZ="$PP_TZ"
ln -sf "/usr/share/zoneinfo/$PP_TZ" /etc/localtime
# 모든 JVM 프로세스 통일
export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Duser.language=$lang -Duser.country=$country -Duser.timezone=$PP_TZ"
These are also recorded in /etc/locale.conf, /etc/environment, and /etc/timezone to be maintained during re-login or tool calls.
The language is determined once at boot and cannot be changed via cookies / Accept-Language.
Toggling ko↔en requires changing PP_LANG + restarting container/Tomcat.
2.3 LOCALE in native mode (setenv.sh)
In native mode, the LOCALE of server/bin/setenv.sh must be dynamic — there were cases where the old hardcoded -Duser.language=ko -Duser.country=KR took precedence over JAVA_TOOL_OPTIONS, causing ko to win.
# server/bin/setenv.sh
LOCALE="-Duser.language=${PP_LANG:-en} -Duser.country=${PP_COUNTRY:-US} -Duser.timezone=${PP_TZ:-Asia/Seoul}"
And in conf/env.sh: export PP_LANG / export PP_TZ / export JAVA_HOME=/usr/lib/jvm/java-21-openjdk.
3. native conf/env.sh
This is the environment file for the native box. bin/start.sh sources it before startup.
# $PE_HOME/conf/env.sh
export PE_HOME=/opt/kopens/plantpulse-edge
export PE_DATA_DIR=/data1
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk # JDK 21 — class file 65 호환 (필수)
export PP_LANG="${PP_LANG:-en}"
export PP_TZ="${PP_TZ:-Asia/Seoul}"
export CLEAN_ON_STARTUP=false
| Key | Meaning |
|---|---|
PE_HOME | Gateway Root (Path based on script) |
PE_DATA_DIR | Data Separation Directory (Usually separate partition /data1) |
JAVA_HOME | JDK 21 (If unspecified, defaults to UnsupportedClassVersionError) |
PP_LANG / PP_TZ | Language / Timezone (See Chapter 2 above) |
CLEAN_ON_STARTUP | Clear work/temp upon booting |
For the overall native installation structure, refer to the installation guide.
4. Container /etc/kopens/*.env (systemd EnvironmentFile)
In container boxes, the systemd unit reads the following files as EnvironmentFile and injects them as docker -e.
If a file exists, it overrides the default values embedded in the unit.
| File | Key | Creator | Purpose |
|---|---|---|---|
/etc/kopens/version.env | PE_VERSION=<tag> | OTA upgrade.sh | Fixes image tag — OTA/rollback changes only this line |
/etc/kopens/i18n.env | PP_LANG / PP_TZ | install.sh | Language / Timezone |
/etc/kopens/cert.env | CERT_PASS | TLS Issuance Script | Keystore password |
# plantpulse-edge.service (발췌)
EnvironmentFile=-/etc/kopens/version.env
EnvironmentFile=-/etc/kopens/i18n.env
EnvironmentFile=-/etc/kopens/cert.env
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!
To change the language:
sudo tee /etc/kopens/i18n.env <<'EOF'
PP_LANG=ko
PP_TZ=Asia/Seoul
EOF
sudo systemctl restart plantpulse-edge.service
For a full breakdown of the systemd unit, see Docker Installation §4.
5. Configuration Directory Locations — -Dpe.conf.dir
These are core env/system properties that determine where to read app.properties and log4j2.xml.
In WAR mode (2026-06-13~), configurations are read directly from the host /etc/kopens — no image/batch rebuild is required.
| Mode | -Dpe.conf.dir | Canonical File |
|---|---|---|
| Container | /opt/kopens/plantpulse-edge/conf (= host /etc/kopens/conf bind-mount) | /etc/kopens/app.properties |
| native | $PE_HOME/conf | $PE_HOME/conf/app.properties |
- Container: Host
/etc/kopens/conf/app.propertiesis the actual file and source-of-truth./etc/kopens/app.propertiesis a symlink pointing to it (ln -sfn /etc/kopens/conf/app.properties /etc/kopens/app.properties). log4j2.xmlis a code artifact — the entrypoint copies it from the webapp default upon each boot (changes are reflected by changing only the log level and restarting; no image rebuild needed).
For the configuration keys themselves (what to write), see the app.properties Reference and Web UI System Configuration. This page covers where and how those files are read.
6. env override during install.sh installation
Box site values, networks, and credentials are injected as env variables at the time of installation (install.sh is reflected in app.properties).
sudo PROFILE=production \
EDGE_ID=EDGE_00303 \
SITE_ID=SITE_00001 \
SERVER_HOST=192.168.0.41 \
SERVER_API_KEY='<platform-api-key>' \
PP_LANG=en PP_TZ=Asia/Seoul \
CERT_PASS='<keystore-pass>' \
bash install.sh
| Variable | Meaning | Default |
|---|---|---|
PROFILE | production / staging / standalone / airgap preset | — |
EDGE_ID | Unique Box ID (^EDGE_[A-Z0-9_]{1,60}$) | Auto-generated based on MAC |
SITE_ID | Site ID (Required for Platform registration) | SITE_00001 |
DEV_MODE | EDGE (Connected to Platform) / STANDALONE (Not connected) | EDGE |
SERVER_HOST / SERVER_API_KEY | Platform API Integration (Required for EDGE mode) | — |
PP_LANG / PP_TZ | Language / Timezone | en / Asia/Seoul |
ADMIN_PASS / API_KEY / MQTT_PASS / OPCUA_PASS | Initial credential override | Random per box |
CERT_PASS | TLS keystore password | Randomly generated during installation |
CERT_DOMAIN / CERT_SAN_DNS / CERT_SAN_IP | Certificate CN / SAN | plantpulse.io / Auto |
NET1_IFACE / NET2_IFACE …, NET2_IP / NET2_GATEWAY / NET2_DNS | NIC Mapping / static IP | Auto-detect / DHCP |
IMAGE_TAG | Fix docker image tag | latest |
SKIP_PULL=1 | Skip docker pull (airgap/re-run) | 0 |
SKIP_COSIGN_VERIFY=1 | Skip image signature verification during installation. This does not apply to OTA upgrades | 0 (staging/standalone 1) |
dev.modesupports bothEDGEandSTANDALONE, and only rejectsPLATFORM. DuringSTANDALONE, theSERVER_HOST/SERVER_API_KEYprompts are skipped (server.hostblank →DiagnosticSenderinit skip).
7. JVM heap env (Container)
| env | Component | Default | Override Example |
|---|---|---|---|
HIVEMQ_HEAP | HiveMQ | -Xms2g -Xmx2g | 1g |
CASSANDRA_HEAP | Cassandra | Auto based on host /proc/meminfo (~1/4) | 1g |
TOMCAT_HEAP | Tomcat | -Xms2g -Xmx2g | 1g |
Drop-in overrides for 8GB boxes (override.conf) are located in Container Mode Resource Limits.
8. Applying Changes
| Changed Item | How to Apply |
|---|---|
PP_LANG / PP_TZ (i18n.env / env.sh) | Restart Container/Tomcat (locale is boot-once) |
CERT_PASS (cert.env) | Restart Container (entrypoint re-patches keystore password) |
PE_VERSION (version.env) | systemctl restart plantpulse-edge.service |
app.properties (Container) | config.sh --set … && config.sh --restart |
app.properties (native) | vi conf/app.properties → bin/restart-app.sh (App only, 30~90s) |
Writing PP_LANG in app.properties will not apply it — language is determined by the env layer (env.sh / i18n.env). Conversely, runtime keys like cassandra.host should be written in app.properties, not env.
9. Common Pitfalls
| Symptom | Cause / Solution |
|---|---|
| UI language does not change | Written in app.properties or no restart. Restart after modifying i18n.env (container)/env.sh (native) |
| ko continues to win in native | setenv.sh LOCALE is hardcoded. Make it dynamic (${PP_LANG}, see 2.3 above) |
UnsupportedClassVersionError | JAVA_HOME is not JDK 21 |
| OPC-UA keystore password error | Mismatch between CERT_PASS and opc.ua.server.keystore.password=${ENV:CERT_PASS:} mirror in cert.env |
Password exposed in ps/logs | Source root 0600 env file or use *_FILE instead of argv |
| Configuration changes not reflected | Did not fix the canonical file pointed to by -Dpe.conf.dir (Container=/etc/kopens, native=$PE_HOME/conf) |
10. Next Documents
- app.properties Reference — All runtime configuration keys
- System Configuration Screen (
/ui/system/config) — Edit app.properties via web - Installation Guide / Docker Installation
- Quick Installation (
install.sh) — env injection procedure during installation - Container Mode Operations — config.sh / Resource Limits / OTA