Skip to main content

Installation Overview

PlantPulse AI runs as a single docker compose stack. The source of truth is compose/docker-compose.yml, and every operational script works on the containers through this file.

For most cases, this one line is all you need
curl -fsSL https://product.kopens.io/plantpulse-ai/install.sh | sudo bash

See One-line Installation for details. This page explains what gets installed where and what the installation script actually does.

Where it gets installed​

/opt/kopens/
├── plantpulse-ai-docker/ ← 설치 본체 (bin · compose)
├── plantpulse-ai-inference-server/ ← 형제 저장소
└── plantpulse-timeseries-insight-server/ ← 형제 저장소
The two sibling repositories «must» be present

compose/docker-compose.yml bind-mounts files from both repositories using relative paths (e.g., the gateway configuration litellm-config.yaml). Without them the stack will not come up — even with START=1, the installer refuses to start and prints the names of the missing repositories. The one-line installation and the air-gapped bundle fetch and extract all three together, so there is nothing to worry about; but if you git clone by hand, you must place all three as sibling directories.

WhatWhere
Installation body/opt/kopens/plantpulse-ai-docker
Configuration/credential source of truth/etc/kopens/plantpulse-ai.env (0600)
Its source template<설치 본체>/compose/env.gb10.example (env.g7e.example for AWS g7e)
Hardware profile<설치 본체>/compose/profiles/gb10.env · g7e.env — the GPU portion. PP_HW_PROFILE in the host env selects it
Docker storage root/data1/docker-data (change with DOCKER_DATA_DIR)

What comes up — twenty-five containers​

GroupContainerWhat it does
Entry pointplantpulse-proxynginx (non-root). Accepts the host's 80 · 443 and the admin console HTTPS (5960) and forwards them inward
UIplantpulse-copilot-web · plantpulse-admin-webCopilot UI · admin console (HTTP 5959)
Inferenceplantpulse-llm · plantpulse-emb · plantpulse-rerankvLLM — generation · embedding · reranking
Gatewayplantpulse-litellm (4000)Consolidates model calls in one place
Documentsplantpulse-rag-api · plantpulse-lightrag · plantpulse-docling-serve · plantpulse-document-understanding-apiReads, splits, and searches documents
Knowledgeplantpulse-neo4j (7474 · 7687) · plantpulse-qdrantKnowledge graph · vector index
Digital threadplantpulse-digital-thread-api (7140, not exposed on the host) · plantpulse-digital-thread-neo4jPlant graph mirror and connector registry · its dedicated Neo4j (not exposed on the host)
Time seriesplantpulse-timeseries-insight-api (8970) · plantpulse-timeseries-insight-server (8980, not exposed on the host)Anomaly detection · forecasting
Visionplantpulse-vision-apiImage understanding
Administrationplantpulse-admin-apiAdmin console backend
Observabilityplantpulse-phoenix (6006, not exposed on the host) · plantpulse-dcgmTrace UI · GPU metrics
Transcriptionplantpulse-asr · plantpulse-asr-audioSpeech → text (vLLM Whisper) · a one-shot service that moves its audio library onto a volume and exits. Only comes up on installations with video · audio enabled (PI_MEDIA_ENABLED=true) → Video · Audio Collection
Registryplantpulse-registry · plantpulse-registry-uiInternal image store for air-gapped networks — does not come up in the default startup

By default, twenty-one come up — everything except the two transcription and two registry containers. With video · audio enabled, the two transcription containers are added, making twenty-three (it is normal for plantpulse-asr-audio to finish its work and remain as «Exited (0)»). The two digital thread containers are new in 2026.10.

8970 and 7115 are the endpoints MCP clients connect to directly (Platform CEP anomaly detection, Claude Desktop, and the like). Everything else sits behind the proxy.

What install.sh does​

The one-line installation is simply bootstrap.sh fetching the repository and running this script. After detecting the OS (RHEL family / Debian family), it proceeds in the order of the step numbers printed in the log.

StepWhat it doesHost-wide
1Locale (ko_KR.UTF-8) · time zone — settles on a single PP_TZ value and aligns the host OS to that time zone as well (value given at install time → value already in env → host time zone (if not UTC) → Asia/Seoul)✅
2Disables swap (also reflected in /etc/fstab)✅
3Installs Docker CE + buildx + the compose v2 plugin — skipped if already present
3bInstalls the NVIDIA driver + container toolkit and registers the nvidia runtime with Docker (skipped if SKIP_GPU=1)
4/etc/docker/daemon.json and the data directories (below)
5OS tuning — chrony · limits · sysctl · tmpfiles✅
6Opens the firewall — opens only the ports compose actually publishes (SSH 22 is opened first). Warns about any of those ports already held by a process outside this stack and tells you which key can move it✅
7Verifies login to the internal registry (skipped if SKIP_LOGIN=1)
8Prepares the configuration sidecar — copies the template to /etc/kopens/plantpulse-ai.env and fills in the values → fetches models, pulls images, and starts only when START=1

Steps marked «Host-wide» change the configuration of the entire host, not just this product. HOST_TUNING governs these four steps (table below).

On a host where other containers are already running, it «only prints»

If you did not pass HOST_TUNING and containers outside this compose project are Up, the default becomes plan — it prints only what would change and writes nothing. If the output looks acceptable to apply, re-run with HOST_TUNING=apply. On a dedicated machine (no other containers) the default is apply. sysctl values are never lowered if the host already has higher ones.

Step 4 — an existing Docker installation is left alone​

Docker on this hostWhat it does
Newly installed by this runWrites daemon.json with the defaults (overlay2, DOCKER_DATA_DIR=/data1/docker-data)
Already presentTreats the running daemon's docker info as the source of truth for storage (data-root · storage-driver) and does not rewrite it. Changes are printed only, and no restart is performed

To change the configuration of an existing Docker installation and allow a restart, pass ALLOW_DOCKER_RECONFIGURE=1. You also need this when switching to a different DOCKER_DATA_DIR.

Changing the storage root makes existing containers and images «disappear»

In the past, missing keys in an existing daemon.json were filled in with defaults, which caused containers and images on hosts using a different storage root to become invisible. That is why the default is now «leave untouched». Use ALLOW_DOCKER_RECONFIGURE=1 only when you understand the consequences.

The default is «preparation only»

Without START=1, it creates the configuration files and stops. This is the default for operators who want to verify the values and bring the stack up themselves.

Commonly used environment variables​

VariableMeaning
START=1Goes all the way to startup instead of stopping after .env preparation — model auto-fetch and installation verification also run at this point
INTERACTIVE=1Prompts for empty values (the default is non-interactive)
ENV_TEMPLATEThe source template for the sidecar. Default env.gb10.example (GB10 · arm64); env.g7e.example (amd64) for AWS g7e. Not selected automatically
ENV_TEMPLATE_SKIP_ARCH_CHECK=1The installer halts if the template and this machine's architecture do not match. Skip this check only when intentional, such as when testing new hardware
HOST_TUNINGskip · plan · apply — host-wide settings (locale · time zone · swap · chrony · limits · sysctl · tmpfiles · firewall) and package installation (Docker CE · NVIDIA driver/toolkit · jq). With skip · plan, no packages are installed and the nvidia runtime is not registered (no Docker restart) — it only prints what it would do and halts at any step that cannot proceed without it. Default is apply, or plan on a host where other containers are running
DOCKER_DATA_DIRThe storage root for Docker newly installed by this run (default /data1/docker-data). An existing Docker installation keeps its own root
ALLOW_DOCKER_RECONFIGURE=1Allows changing daemon.json of an existing (running) Docker installation and restarting it
SKIP_GPU=1Skips step 3b (NVIDIA driver · toolkit installation) — for hosts where the operations team manages drivers separately
SKIP_WAIT=1Skips «wait until responsive» after START=1 and the installation verification
WAIT_READY_MINUpper bound for that wait, in minutes. Default 15
SKIP_LOGIN=1Skips login to the internal registry — for air-gapped networks. Also skips the image pull
SKIP_PULL=1Brings the stack up from images already fetched on an internet-connected machine
INSECURE_REGISTRYAdds the customer's internal registry (HTTP) to the daemon.json allow list
SKIP_PULL=1 can bring up «an old build»

Image tags are YYYY.MM and are refreshed under the same name. A locally cached tag of the same name may be last month's build. If you must pin an exact version, do not use this option.

What it asks for and what it «generates»​

The installer asks for values only a person or the Platform knows, and generates the secrets used only inside the stack itself.

Values
AsksTLS certificate SANs (PP_PROXY_CERT_SAN) · the address and key for obtaining Platform credentials (PP_DATALAKE_ADMIN_URL · PP_DATALAKE_ADMIN_API_KEY) — then each remaining placeholder (Platform API token · administrator password · Neo4j password, etc.) one at a time
GeneratesGateway master key · salt · inference server key · inbound key · RAG key · admin session secret · the two stored-credential encryption keys (PI_COPILOT_SECRET_KEY · PI_DT_SECRET_KEY)

Prompting happens when INTERACTIVE=1. Every prompted value can also be passed as an environment variable of the same name — if you pass it like PP_DATALAKE_ADMIN_URL=… PP_DATALAKE_ADMIN_API_KEY=… PP_API_TOKEN=… PI_ADMIN_PASSWORD=… bash, the installer fills only the keys still left as placeholders with that value (2026.10 re-release — previously only the two values, TLS SAN and Platform credentials, were accepted). Anything still left is filled interactively or by editing /etc/kopens/plantpulse-ai.env directly. Generated values are also written only where a placeholder remains; values already entered by a person are never overwritten. A TLS SAN already written in the env file is likewise left as-is on reinstall — to change it, run with PP_PROXY_CERT_SAN=… specified explicitly.

Platform accounts · passwords are no longer requested (2026.10)

The Platform host (PP_HOST) and the Platform PG · Aster TSDB · Redis accounts · passwords are no longer requested or written to env. The Platform host is derived from the host portion of PP_DATALAKE_ADMIN_URL, and the accounts · passwords are obtained from the Platform at startup by the containers and host scripts. If you pass a value such as PP_PG_USER in this run, it only prints a warning that it is not written to env. The installer does not place the trust anchor root.crt, so prepare it yourself → see "Obtaining Platform Credentials — What to Prepare Before Startup" below.

TLS SAN — put the access addresses in the certificate

The proxy's self-signed certificate must contain the address or name people actually use to connect, or the browser will block it. The installer uses this host's address as the default, but a public IP outside NAT, a load balancer VIP, or a domain name is not visible from inside this box. Interactive installation always prompts for it; non-interactive installation uses only the host address and prints a warning. If you connect by an external name, pass several values separated by commas.

curl -fsSL https://product.kopens.io/plantpulse-ai/install.sh \
| sudo PP_PROXY_CERT_SAN=ai.example.com,203.0.113.10 bash
We do not make people type in eight secrets

Having people transcribe them produces short, duplicated values — there was an actual case where two hosts were both using the example key written in the documentation. Now the installer generates them, and operators only need to rotate them.

If the inbound key is empty, nothing comes up

The inbound key the installer generates (PI_INBOUND_API_KEY) is the shared entry key used by RAG · Time Series Insight · document understanding · vision · digital thread. If it is empty, the pre-start check (bin/lint-env.sh) will not bring the stack up, and each service refuses to start on its own — the only way to come up open without a key is to deliberately pass the development switch PI_INBOUND_AUTH_DISABLED=true. If you created the env file by hand, be sure to fill in this line → Environment Variable Configuration

What happens when START=1 — three more things​

Since 2026.09, the startup path «obtains, fills in, and verifies» values.

StepWhat it does
Placeholder check🔴 Halts if CHANGE_ME_* or <PLACEHOLDER> still remain. Previously it only warned and finished as "installation complete"
Platform credential pre-checkBefore pulling images, it tries obtaining credentials from the Platform once. If the key is wrong or root.crt is missing, it halts at [FATAL] [platform-credentials] … without bringing anything up. It also halts if either value is empty
Model auto-fetchDownloads missing models from model.kopens.ai, verifies the SHA256, and extracts them. This used to be a manual procedure
Readiness waitWaits up to WAIT_READY_MIN (15) minutes for the admin console (:5959/health) and the RAG gateway (:7115/ping) to respond. Exceeding this ends in installation failure. It also reports whether the console HTTPS (:5960) responds, but that is not counted toward the failure verdict
Installation verificationChecks not whether the stack «came up» but whether it «works» (below)
A green stack with empty values is worse

If it starts with placeholders still in place, the screens look fine and only the functionality is dead. That is why START=1 causes it to refuse to start. To prepare only, simply do not pass START (air-gapped installation keeps the flow of filling in values later). From 2026.10, the same check applies when you later bring it up with bin/up.sh — if placeholders remain, it prints the names of the remaining keys and refuses to start.

"21/21 running" does not mean "it works"

There was a case on a clean installation where the status was all running · HTTP 200 while three things were dead — all three pass the container health check.

  • Nobody fetched the models, so only the inference server kept restarting
  • The gateway key was empty, so every chat returned 401
  • The Platform token was a placeholder, so the MCP tools were silently zero

Installation verification (bin/verify-install.sh) looks at these three. Each check answers OK · FAIL · "could not measure", and what could not be measured is not counted as a pass.

Four steps to confirm the installation is «normal»​

  1. bin/status.sh — 21/21 running · HTTP=200 (the twenty-one that come up by default)
  2. bin/verify-install.sh — OK 4 · FAIL 0. A single FAIL ends the installation as a failure
  3. AI Admin Console › MCP Gateway › Servers — built-in servers such as Platform · Time Series Insight · digital thread · engineering calculation should be healthy, with a tool count other than 0. If it is 0, check the Platform token → MCP Tool Map
  4. Ask a question in the chat — there have been cases where 1–3 were all green and only chat returned 401
Two known pitfalls
  • On a host coming up for the first time, you may need to start twice. If the document parser does not become healthy within the dependency wait time, startup stops there. Bringing it up again yields 21/21.
  • 🔴 Running docker compose up directly rolls back the deployed images. Deployment binds the tag to that invocation only and does not leave it in .env (intended behavior). Use the provided scripts when bringing the stack up.

Obtaining Platform credentials — what to prepare before startup​

From 2026.10, the accounts · passwords · ports for Platform PG · Aster TSDB · Redis · RustFS are obtained from the Platform when the containers start (Copilot · Time Series Insight API · LightRAG · RAG API · digital thread · LiteLLM). compose does not pass these values, so if the four items below are not in place, those containers halt with FATAL.

What to prepareWhere it comes from
PP_DATALAKE_ADMIN_URLhttps://<플랫폼 호스트>:4950 — this host is itself PP_HOST
PP_DATALAKE_ADMIN_API_KEYThe line of the same name in /etc/kopens/plantpulse-platform.env on the Platform host
Platform allow list🔴 The AI host's IP must be on the Platform-side allow list to obtain credentials
/etc/kopens/ca/root.crtThe Platform trust anchor — root_pem from the Platform GET /api/credentials/tls response. Place it after comparing the fingerprint against root_fingerprint_sha256 in the same response
/etc/kopens/ca/CA.crtThe same content as root.crt. The applications read it under this name

The first two values go into /etc/kopens/plantpulse-ai.env — they are present in the template as placeholders, and interactive installation prompts for them or you pass them as environment variables. compose mounts the entire /etc/kopens/ca directory into the containers.

  • Both values are required. If empty, compose refuses to start; with only one of them, the containers reject startup.
  • Host scripts (pre-start DB provisioning, etc.) also obtain credentials from the Platform using the same two values and root.crt. The host env contains no Platform accounts or passwords.
  • The server certificate is verified against root.crt only — a missing file is FATAL, and there is no way to disable verification.
  • Check the logs to confirm whether retrieval succeeded — only key names and counts are printed.
docker logs plantpulse-copilot-web 2>&1 | grep credentials:

→ Environment Variable Configuration · Troubleshooting

To use video · audio — one line after installation​

Video · audio collection and asking by voice in chat are off by default. To use them, add the single line PI_MEDIA_ENABLED=true to the host env, fetch the transcription model (Aura-ASR-Turbo, about 1.5 GB) with bin/fetch-models.sh, and then bin/restart.sh. The procedure and verification steps are in Video · Audio Collection.

After installation​

OrderWhat to doCommand · document
1Change the default passwordChanging the Initial Password
2Check whether the stack is alivebin/status.sh · bin/health.sh
3Pinpoint where it is stuckbin/doctor.sh

Frequently used operational commands are in bin/ — up.sh · down.sh · restart.sh · logs.sh · backup.sh · update.sh · passwd.sh. See Service Operations for details.