Skip to main content

Service Operations

The AI stack is a single docker compose. All operational commands live in bin/ of the installation itself, and they call compose on your behalf.

cd /opt/kopens/plantpulse-ai-docker/bin

Commonly used commands​

What you want to doCommandNotes
Bring it up./up.shExit code 0 means «ready»
Bring it down./down.sh
Restart it./restart.sh0 means it restarted and is ready
Check current status./status.shSummary of compose stack status
Run a detailed check./health.shDetailed per-service report
Follow logs./logs.sh
Pull new images and deploy./update.shPulls and restarts
Trust the exit code

up.sh · restart.sh report 0 not when the container has come up, but when it has become usable. You don't need to add a separate wait loop when automating this with a script.

When pinpointing what went wrong​

./doctor.sh

🔴 doctor.sh changes nothing. It only reads and judges. So you can safely run it first during an incident — a human decides what to fix.

Among the lines in the env file, the ones blocking startup are shown as «bad», while old names · duplicate lines that change nothing even if left alone (the pre-startup check's [warn] — alias · oldname · media-same) are shown separately as «old or redundant lines». The latter are not the cause of the failure. bin/lint-env.sh prints the command to clean them up → Environment Variable Configuration

Working with a single container​

If you don't need to shake up the whole stack, give the script the service name.

cd /opt/kopens/plantpulse-ai-docker/bin
./status.sh # 무엇이 떠 있나
./restart.sh plantpulse-rag-api # 0 이면 재시작 후 준비됨
./logs.sh plantpulse-lightrag
Do not call docker compose by hand

The compose file lives under compose/, and the configuration is in /etc/kopens/plantpulse-ai.env. Running docker compose … bare from the installation directory either can't find the file, or runs with a different configuration even if it can — with up this can roll back to the deployed image. The scripts call it specifying both the compose file and the configuration file explicitly.

Container names and their respective roles are listed in the table in Installation Overview.

If you need to get inside a container:

bin/shell.sh plantpulse-rag-api

Logs​

./logs.sh # 스택 전체를 따라 본다
./logs.sh plantpulse-llm # 서비스 하나

Container logs are rotated by Docker (configured in compose). No files pile up on the host that you need to clean up separately.

Containers run as non-root​

As of 2026.10, every service in compose explicitly specifies its runtime user (user:), and the only one that runs as root is plantpulse-dcgm, which reads GPU metrics (root and SYS_ADMIN are needed to read GPU profiling fields).

WhatRuntime user (uid:gid)
Our images — ingress proxy · copilot · admin console · RAG · time series insight · document understanding · vision · digital thread, etc.10001:0
vLLM (plantpulse-llm · -emb · -rerank, and plantpulse-asr for installations with video · audio transcription enabled) · plantpulse-asr-audio10001:0
Both Neo4j instances7474:7474
Qdrant1000:1000
Phoenix65532:65532
Docling1001:0
plantpulse-dcgmroot (exception)

With the ingress proxy (plantpulse-proxy) also becoming non-root, the container's internal ports changed to 8080 · 9443 · 8443. The ports visible on the host — 80 · 443 · 5960 — remain unchanged → Service Port Map

Write volumes created as root on older installations cannot be written to by non-root processes. Docker does not change the ownership of an existing volume, and write failures are usually either silent (e.g., appearing only on console instead of a log file) or immediately fatal (Qdrant dies instantly on a root-owned store). That's why the startup scripts that up.sh · restart.sh · update.sh go through call bin/volumes-nonroot.sh before bringing compose up.

ServiceWhat gets fixedOwner it's set to
plantpulse-copilot-web · plantpulse-timeseries-insight-apiApp logs10001:0
plantpulse-timeseries-insight-serverApp logs · per-installation fine-tuned weights10001:0
plantpulse-lightragIndex store · source input10001:0
vLLM set (transcription plantpulse-asr only on installations with it enabled)Compile cache10001:0
plantpulse-asr-audio (only on installations with it enabled)Audio library volume (pi-asr-audio)10001:0
plantpulse-neo4j · plantpulse-digital-thread-neo4jData7474:7474
plantpulse-qdrantVector store1000:1000
plantpulse-phoenixData65532:65532
plantpulse-proxyCertificates (pp-proxy-certs)10001:0
plantpulse-registry (only when the profile is enabled)Image store10001:0

It only changes ownership if there's even one file not owned by that service's user, so it does the work once at first, and merely checks on subsequent runs. If it can't fix ownership, it halts startup (because starting with incorrect ownership causes writes to fail silently).

Calling docker compose up by hand skips this step

On hosts brought up on 2026.09 or earlier, the first startup must be done with bin/up.sh (or update.sh · restart.sh).

What doesn't come up by default — compose profiles​

ProfileContainersUse
registryplantpulse-registry · plantpulse-registry-uiLocal image store for the air-gapped network hub node
mediaplantpulse-asr · plantpulse-asr-audioVideo · audio transcription (including asking by voice). Enabled with a single line, PI_MEDIA_ENABLED=true, in the host env — the operational scripts (up.sh · restart.sh · status.sh · logs.sh …) check that value and attach the profile → Video · Audio Collection

For media, don't give the profile name directly — enable it with PI_MEDIA_ENABLED instead. That switch also aligns the values for rag-api · document understanding, as well as fetching the transcription model. If you write COMPOSE_PROFILES in the host env, the pre-startup check will bring startup up in a unknown state.

plantpulse-asr-audio is a one-shot service that moves the audio library to a volume and then exits. «Exited (0)» in status.sh is normal and counts as healthy. If it exits with a non-zero code, plantpulse-asr will not come up either.

Backups and passwords​

What you want to doCommandDocumentation
Take a backup./backup.sh (what it covers is listed in ./backup.sh --list — logs · cache and the transcription audio library volume are excluded)Database Management
Change passwords · keys./passwd.shChanging Passwords · API Keys
Remove the installation./remove.shCannot be undone

Databases use the platform's​

PostgreSQL · Aster TSDB · Redis (Valkey) · RustFS are not started by the AI stack. They share the platform installation (host of PP_DATALAKE_ADMIN_URL). If these four go down, AI stops too, so during an incident, first use the Service Port Map to determine which side the problem is on.

As of 2026.10, those accounts · passwords are fetched from the platform when the container starts. If the platform is unreachable, the container doing the fetch halts with FATAL, and if the password was changed on the platform, simply restarting the affected container resolves it → Environment Variable Configuration