env Environment Configuration — env.sh / PPLANG / CERTPASS / -Dpe.conf.dir
The passwords shown in this document (CERT_PASS and the like) are defaults for the in-house dev box and are not secrets.
Do not use them as-is in customer or production deployments:
install.shgenerates a 14-character random value whenCERT_PASSis not specified (bin/install.sh:298). The generated value is written to/etc/kopens/credentials.txt(0600, root) and to the factory label.- Change the box OS login password at installation time as well.
- The defaults in this document are therefore valid only for a single in-house dev box.
PlantPulse Edge configuration is split into two layers.
| Layer | What | Where |
|---|---|---|
| Environment variables (env) ← this page | Language/time zone, JDK path, TLS password, data paths, site values set at install time | env.sh / /etc/kopens/*.env / systemd / docker -e |
| Application settings (properties) | Runtime keys such as edge.id / cassandra.* / mqtt.* / OPC-UA | app.properties → app.properties guide |
Once the env layer determines the language, time zone, JVM options, passwords, and configuration directory location, app.properties runs on top of it. These are values you set once per box and rarely change.
1. Key env variables at a glance
| Variable | Meaning | Default |
|---|---|---|
PP_LANG | UI/OS/JVM language (BCP 47) — one language per box | en |
PP_TZ | Time zone (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 (shared by Tomcat/HiveMQ/OPC-UA) | kopens123! (legacy) / random at install time |
CLEAN_ON_STARTUP | Whether to clean work/temp at boot | false |
JAVA_TOOL_OPTIONS | Options common to all JVMs (entrypoint/setenv.sh injects language/time zone) | (auto-configured) |
CERT_PASS / SERVER_API_KEY and any password must not be exposed in shell history, ps argv, or logs.
Use *_FILE injection (e.g. EDGE_ADMIN_PASSWORD_HASH_FILE, CASSANDRA_PASSWORD_FILE) or source a root 0600 file before running the installation. Random credentials generated by install.sh are stored once in /etc/kopens/credentials.txt (chmod 0600 root).
2. Language / time zone — PP_LANG / PP_TZ
PlantPulse enforces a single language and time zone per box. Per-user cookies and Accept-Language are ignored; the webapp, host OS, container OS, and JVM all use the same values.
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 layers)
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 a container
container-entrypoint.sh maps PP_LANG to a POSIX locale and builds the 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"
The values are also written to /etc/locale.conf, /etc/environment, and /etc/timezone, so they persist across re-logins and tool invocations.
The language is determined once at boot and cannot be changed via cookies or Accept-Language.
Toggling ko↔en requires changing PP_LANG and restarting the container/Tomcat.
2.3 LOCALE in native mode (setenv.sh)
In native mode, LOCALE in server/bin/setenv.sh must be dynamic — there was a case where the old hardcoded -Duser.language=ko -Duser.country=KR took precedence over JAVA_TOOL_OPTIONS and ko won.
# server/bin/setenv.sh
LOCALE="-Duser.language=${PP_LANG:-en} -Duser.country=${PP_COUNTRY:-US} -Duser.timezone=${PP_TZ:-Asia/Seoul}"
Then add export PP_LANG / export PP_TZ / export JAVA_HOME=/usr/lib/jvm/java-21-openjdk to conf/env.sh.
3. native conf/env.sh
This is the environment file for a 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 (base path for scripts) |
PE_DATA_DIR | Separate data directory (usually a dedicated partition /data1) |
JAVA_HOME | UnsupportedClassVersionError if JDK 21 is not specified |
PP_LANG / PP_TZ | Language / time zone (see section 2) |
CLEAN_ON_STARTUP | Clean work/temp at boot |
For the full native installation layout, see Native installation.
4. Container /etc/kopens/*.env (systemd EnvironmentFile)
On a container box, the systemd unit reads the following files as EnvironmentFile and injects them as docker -e.
If a file exists, it overrides the unit's built-in defaults.
| File | Key | Created by | Purpose |
|---|---|---|---|
/etc/kopens/version.env | PE_VERSION=<tag> | OTA upgrade.sh | Pins the image tag — OTA/rollback changes only this one line |
/etc/kopens/i18n.env | PP_LANG / PP_TZ | install.sh | Language / time zone |
/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 location — -Dpe.conf.dir
This is the key env/system property that determines where app.properties and log4j2.xml are read from.
In WAR mode (2026-06-13 onward), configuration is read directly from the host /etc/kopens — no image or 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: the host
/etc/kopens/conf/app.propertiesis the real file and the source of truth./etc/kopens/app.propertiesis a legacy-compatibility 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 at every boot (change the log level and restart to apply; no image rebuild needed).
For the configuration keys themselves (what to write), see the app.properties guide and the Environment configuration screen. This page covers where and how those files are read.
6. env overrides at install.sh time
At installation time, site values, network settings, and credentials for the box are injected as env vars (install.sh reflects them into 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 | Box-unique ID (^EDGE_[A-Z0-9_]{1,60}$) | Auto-generated from MAC |
SITE_ID | Site ID (required for platform registration) | SITE_00001 |
DEV_MODE | EDGE (platform connected) / STANDALONE (not connected) | EDGE |
SERVER_HOST / SERVER_API_KEY | Platform API integration (required in EDGE mode) | — |
PP_LANG / PP_TZ | Language / time zone | en / Asia/Seoul |
ADMIN_PASS / API_KEY / MQTT_PASS / OPCUA_PASS | Initial credential overrides | Random per box |
CERT_PASS | TLS keystore password | kopens123! (legacy) / random |
CERT_DOMAIN / CERT_SAN_DNS / CERT_SAN_IP | Certificate CN / SAN | plantpulse.io / automatic |
NET1_IFACE / NET2_IFACE …, NET2_IP / NET2_GATEWAY / NET2_DNS | NIC mapping / static IP | Auto-detected / DHCP |
IMAGE_TAG | Pin the docker image tag | latest |
SKIP_PULL=1 | Skip docker pull (air-gapped/re-run) | 0 |
SKIP_COSIGN_VERIFY=1 | Skip image signature verification | 0 (staging/standalone 1) |
dev.modeaccepts bothEDGEandSTANDALONE; onlyPLATFORMis rejected. WithSTANDALONE, theSERVER_HOST/SERVER_API_KEYprompts are skipped (server.hostblank →DiagnosticSenderinit skipped).
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 |
The drop-in override for an 8GB box (override.conf) is described in Container mode §Resource limits.
8. Applying changes
| What you changed | How to apply |
|---|---|
PP_LANG / PP_TZ (i18n.env / env.sh) | Restart the container/Tomcat (locale is resolved once at boot) |
CERT_PASS (cert.env) | Restart the container (the entrypoint re-patches the 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.sh (~6 s) |
Writing PP_LANG in app.properties has no effect — the language is determined by the env layer (env.sh / i18n.env). Conversely, runtime keys such as cassandra.host belong in app.properties, not in env.
9. Common pitfalls
| Symptom | Cause / fix |
|---|---|
| UI language does not change | Written in app.properties, or no restart. Edit i18n.env (container) / env.sh (native) and restart |
| ko keeps winning on native | LOCALE in setenv.sh is hardcoded. Make ${PP_LANG} dynamic (see 2.3) |
UnsupportedClassVersionError | JAVA_HOME is not JDK 21 |
| OPC-UA keystore password error | CERT_PASS in cert.env does not match the opc.ua.server.keystore.password=${ENV:CERT_PASS:} mirror |
Password exposed in ps/logs | Use *_FILE or source a root 0600 env file instead of argv |
| Configuration changes not applied | The canonical file pointed to by -Dpe.conf.dir was not edited (container = /etc/kopens, native = $PE_HOME/conf) |
10. Next documents
- app.properties guide — all runtime configuration keys
- Environment configuration screen (
/ui/system/config) — edit app.properties from the web UI - Native installation / Docker (container) installation details
- Quick installation (
install.sh) — env injection procedure at install time - Container mode operations guide — config.sh / resource limits / OTA