Database Management
Database Configuration
PlantPulse AI uses six databases, each serving a different purpose.
| Database | Use | Data Type |
|---|---|---|
| PostgreSQL | Relational metadata | User info, chat history, RAG metadata, feature store |
| Aster TSDB | Time series storage | Sensor data, copilot tasks, inference history |
| Neo4j (two instances) | Graph data | LightRAG knowledge graph · Digital Thread's plant graph mirror |
| Qdrant | Vector storage | Document embeddings, equipment feature vectors |
| Valkey (Redis) | Cache | Sessions, temporary data, search cache |
| RustFS | Object storage | Document files, images, reports |
PostgreSQL
Database List
The AI does not run its own PostgreSQL — it creates and uses four DBs inside the Platform's PostgreSQL
(it creates provision-platform-db.sh if absent, and skips it if present — idempotent).
| Database | Services Using It |
|---|---|
pi | DB for AI apps — one schema per app: co = AI Chat Web (users · chat history · apps · settings), dt = Digital Thread (connector registry) |
lightrag | LightRAG's document processing state (doc_status) |
rag_ingest | State of the document ingestion pipeline (rag-api). When drawing · video collection is enabled, those tables (rag_drawing_* · rag_media_*) are also created here at startup |
litellm | Keys and usage for the LiteLLM gateway |
The connection account is the Platform PG account (PP_PG_USER) — there is no separate account for AI.
The two schemas of pi also connect with this account. The table is not placed in public.
Upgrading from 2026.09 or earlier — ch is moved to pi.co
In 2026.09 and earlier, the copilot table was in public of DB ch. In 2026.10, at startup
bin/migrate-pg-ch-to-pi.sh runs before compose and moves this — since install · up · update ·
compose-up all follow the same sequence, there is no need to call it separately.
- Stop the copilot · Digital Thread containers.
- Clean up remaining sessions in
ch— terminate only the sessions of the app account (PP_PG_USER) attached toch. - In a single transaction, move the objects of
publicto the new schemaco. ALTER DATABASE ch RENAME TO pi.
| State | Action |
|---|---|
Only pi exists · neither exists | Nothing to do |
Only ch exists | Migrate as described above |
Both ch and pi exist | Refuse and change nothing |
Objects exist in both public and co of ch | Refuse (sign of a manual half-migration) |
cd /opt/kopens/plantpulse-ai-docker
bin/migrate-pg-ch-to-pi.sh --check # 미리 보기 — 아무것도 바꾸지 않습니다
bin/migrate-pg-ch-to-pi.sh --rollback # 되돌리기 — pi.co → ch.public, 이름을 ch 로
Take a dump of ch into pg_dump once before upgrading. Disk and retention for Platform PG is the Platform's responsibility,
and this script does not substitute for it. After rolling back, you must start up with the previous version's compose and copilot image —
starting with the new compose will immediately migrate again.
Management
# PostgreSQL 접속 — 코파일럿 스키마
psql -h <플랫폼 PG 호스트> -U <PP_PG_USER> -d pi -c 'SET search_path TO co' -c '\dt'
# 디지털 스레드 스키마
psql -h <플랫폼 PG 호스트> -U <PP_PG_USER> -d pi -c '\dt dt.*'
# 데이터베이스 목록 확인
psql -h <플랫폼 PG 호스트> -U <PP_PG_USER> -c '\l'
Aster TSDB
Keyspaces
| Keyspace | Use |
|---|---|
pi | PlantPulse AI main (copilot tasks, inference history) |
Management
# Cassandra CQL 접속
cqlsh 127.0.0.1 9042 -u cassandra -p cassandra
# 키스페이스 목록
DESCRIBE KEYSPACES;
# 테이블 목록
USE pi;
DESCRIBE TABLES;
The Aster TSDB keyspace is automatically created by the db/cassandra/support/keyspace-create.sh script on first start.
Neo4j
Starting in 2026.10, two Neo4j instances run. Same image, same account (PI_NEO4J_USER · PI_NEO4J_PASSWORD),
but what they hold and where they are exposed differ.
| Container | Holds | Volume | Host Port |
|---|---|---|---|
plantpulse-neo4j | LightRAG knowledge graph | pi-neo4j-data | 7474 · 7687 |
plantpulse-digital-thread-neo4j | Digital Thread's plant graph mirror (Dt*) — reference only, no body text | pi-digital-thread-neo4j-data | None |
Neo4j Community cannot create additional DBs or split permissions by role, so the instances were separated to clearly isolate the two services. On the Digital Thread side, only the Digital Thread API connects within the compose network, and memory limits (heap 512 MB · page cache 256 MB) are enforced.
bin/passwd.sh changes passwords on both instances togetherSince there is a single account, do not change only one side → Changing Passwords · API Keys
Connection Info — plantpulse-neo4j
| Item | Value |
|---|---|
| Browser URL | http://<server IP>:7474 |
| Bolt URI | bolt://127.0.0.1:7687 |
| User | neo4j |
| Password | neo4j123 |
Management
Manage the graph with Cypher queries in Neo4j Browser (http://<server IP>:7474):
// 노드 타입별 카운트
MATCH (n) RETURN labels(n), count(n) ORDER BY count(n) DESC;
// 관계 타입별 카운트
MATCH ()-[r]->() RETURN type(r), count(r) ORDER BY count(r) DESC;
Digital Thread Side — plantpulse-digital-thread-neo4j
Since the port is not exposed, it cannot be accessed via Browser. Check status on the Digital Thread → Graph Mirror screen of the admin console (Neo4j probe · node counts by label). The mirror is a copy that can be rebuilt from the source (Platform ledger · events) → Digital Thread
Qdrant
Connection Info
| Item | Value |
|---|---|
| REST API | http://127.0.0.1:6333 |
| Dashboard | http://127.0.0.1:6333/dashboard |
| API Key | qdrant-key-123 |
Collections
| Collection | Use |
|---|---|
| (auto-created by LightRAG) | RAG document vectors (managed by LightRAG) |
RustFS
Connection Info
| Item | Value |
|---|---|
| API | http://127.0.0.1:9000 |
| Console | http://127.0.0.1:9001 |
| Access Key | minio |
| Secret Key | minio123! |
Buckets
| Bucket | Use |
|---|---|
plantpulse-rag | A single object store for rag-api. Separated by prefix — original documents manually uploaded via manual/ admin console, drawings/ drawing structures and page previews (when drawing collection is enabled), media/ video · audio playback copies and transcripts (when video collection is enabled), migration/ markers for completed migration from the old bucket. The bucket name is the compose default, and only when two AI installs share the same Platform does the second install get a different name via the host env RAG_S3_BUCKET → Environment Variable Configuration |
Older versions kept manually uploaded originals in the rag-manual-docs bucket. In 2026.10, rag-api copies those objects
in the background at startup to under manual/ of plantpulse-rag. If the object already exists at the new location, it is not
overwritten, and since the old bucket is read-only, rolling back to an old image still reads it correctly. Check
progress under the object_store_migration entry of rag-api /api/v1/config.
Data Storage Paths
Local state for the AI stack lives in named docker volumes, not host directories.
Do not try to locate them by host path — check with docker volume inspect.
| Volume | Contents | If Lost |
|---|---|---|
pi-lightrag-inputs | Uploaded original documents | No way to regenerate the index |
pi-lightrag-storage | LightRAG KV cache (local JSON) | Can be regenerated |
pi-qdrant-data | Document embedding vectors | Can be regenerated from the originals |
pi-neo4j-data | LightRAG knowledge graph | Can be regenerated from the originals |
pi-digital-thread-neo4j-data | Digital Thread's plant graph mirror | Can be rebuilt from the source — though restoring is much faster since it only needs to re-read Platform events |
pi-ttm-tuned-models | Time series models trained per installation on field data | Cannot be rebuilt without that field data |
pi-pp-proxy-certs | Proxy's TLS certificate | If it's a real certificate the customer provided, it must be re-obtained |
pi-phoenix-data | LLM call traces | Past traces are lost |
pi-registry-data | Local image registry (only nodes with the registry profile enabled) | Images must be re-pushed |
pi-asr-audio | Audio library read by transcript serving (only installs with video · audio enabled) | Nothing lost — plantpulse-asr-audio rewrites it every time it starts |
What bin/backup.sh Includes
It includes every volume declared in the compose file — there is no separately maintained list. The only exclusions
are logs and caches that can be regenerated (copliot-web-logs · insight-api-logs · llm-cache · emb-cache ·
rerank-cache · asr-cache), and the transcript audio library that is recreated every startup (asr-audio). registry-data only exists
on nodes with that profile enabled, so it is included if present and skipped if not.
cd /opt/kopens/plantpulse-ai-docker/bin
./backup.sh --list # 볼륨마다 backup · optional · exclude 판정만 찍는다
./backup.sh # 전부 → /data1/pp-backup/YYYYMMDD/<볼륨>.tar.gz
COMPONENT=neo4j-data ./backup.sh # 하나만
| Flag | Default | Meaning |
|---|---|---|
BACKUP_ROOT | /data1/pp-backup | Where to place backups |
BACKUP_RETENTION_DAYS | 30 | Delete date directories older than this — only when that round fully succeeded |
The backup.sh from before 2026.09 included only four manually listed volumes (neo4j-data · qdrant-data · lightrag-storage ·
lightrag-inputs), and the TLS certificate volume was missing. Now the list is pulled from compose, so
new volumes are backed up by default when they appear.
It may be a copy taken mid-write. If you need a consistent backup, such as right before an upgrade, bring it down with bin/compose-down.sh
and then run it. Restoring should also be done with compose down (an example is in the bin/backup.sh preamble).
PostgreSQL · Aster TSDB · Redis · RustFS are Platform-owned, so this script does not back them up — follow the Platform's own backup procedures.
Backup Recommendations
| Target | Method | Frequency |
|---|---|---|
| PostgreSQL | pg_dump | Daily |
| Neo4j | neo4j-admin dump | Daily |
| RustFS | rc mirror | Daily |
| Qdrant | Snapshot API | Weekly |
/etc/kopens/plantpulse-ai.env | File copy — 🔴 PI_LITELLM_SALT_KEY is here | On change |
| AI local volumes | bin/backup.sh (above) | Daily |