Skip to main content

Database Management

Database Configuration​

PlantPulse AI uses six databases, each serving a different purpose.

DatabaseUseData Type
PostgreSQLRelational metadataUser info, chat history, RAG metadata, feature store
Aster TSDBTime series storageSensor data, copilot tasks, inference history
Neo4j (two instances)Graph dataLightRAG knowledge graph · Digital Thread's plant graph mirror
QdrantVector storageDocument embeddings, equipment feature vectors
Valkey (Redis)CacheSessions, temporary data, search cache
RustFSObject storageDocument 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).

DatabaseServices Using It
piDB for AI apps — one schema per app: co = AI Chat Web (users · chat history · apps · settings), dt = Digital Thread (connector registry)
lightragLightRAG's document processing state (doc_status)
rag_ingestState 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
litellmKeys 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.

  1. Stop the copilot · Digital Thread containers.
  2. Clean up remaining sessions in ch — terminate only the sessions of the app account (PP_PG_USER) attached to ch.
  3. In a single transaction, move the objects of public to the new schema co.
  4. ALTER DATABASE ch RENAME TO pi.
StateAction
Only pi exists · neither existsNothing to do
Only ch existsMigrate as described above
Both ch and pi existRefuse and change nothing
Objects exist in both public and co of chRefuse (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 로
The migration script does not take a dump

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​

KeyspaceUse
piPlantPulse 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;
info

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.

ContainerHoldsVolumeHost Port
plantpulse-neo4jLightRAG knowledge graphpi-neo4j-data7474 · 7687
plantpulse-digital-thread-neo4jDigital Thread's plant graph mirror (Dt*) — reference only, no body textpi-digital-thread-neo4j-dataNone

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 together

Since there is a single account, do not change only one side → Changing Passwords · API Keys

Connection Info — plantpulse-neo4j​

ItemValue
Browser URLhttp://<server IP>:7474
Bolt URIbolt://127.0.0.1:7687
Userneo4j
Passwordneo4j123

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​

ItemValue
REST APIhttp://127.0.0.1:6333
Dashboardhttp://127.0.0.1:6333/dashboard
API Keyqdrant-key-123

Collections​

CollectionUse
(auto-created by LightRAG)RAG document vectors (managed by LightRAG)

RustFS​

Connection Info​

ItemValue
APIhttp://127.0.0.1:9000
Consolehttp://127.0.0.1:9001
Access Keyminio
Secret Keyminio123!

Buckets​

BucketUse
plantpulse-ragA 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
rag-manual-docs from before 2026.10 is migrated automatically

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.

VolumeContentsIf Lost
pi-lightrag-inputsUploaded original documentsNo way to regenerate the index
pi-lightrag-storageLightRAG KV cache (local JSON)Can be regenerated
pi-qdrant-dataDocument embedding vectorsCan be regenerated from the originals
pi-neo4j-dataLightRAG knowledge graphCan be regenerated from the originals
pi-digital-thread-neo4j-dataDigital Thread's plant graph mirrorCan be rebuilt from the source — though restoring is much faster since it only needs to re-read Platform events
pi-ttm-tuned-modelsTime series models trained per installation on field dataCannot be rebuilt without that field data
pi-pp-proxy-certsProxy's TLS certificateIf it's a real certificate the customer provided, it must be re-obtained
pi-phoenix-dataLLM call tracesPast traces are lost
pi-registry-dataLocal image registry (only nodes with the registry profile enabled)Images must be re-pushed
pi-asr-audioAudio 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 # 하나만
FlagDefaultMeaning
BACKUP_ROOT/data1/pp-backupWhere to place backups
BACKUP_RETENTION_DAYS30Delete date directories older than this — only when that round fully succeeded
Previously only four volumes were included

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.

A backup taken while running may not be fully consistent

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​

TargetMethodFrequency
PostgreSQLpg_dumpDaily
Neo4jneo4j-admin dumpDaily
RustFSrc mirrorDaily
QdrantSnapshot APIWeekly
/etc/kopens/plantpulse-ai.envFile copy — 🔴 PI_LITELLM_SALT_KEY is hereOn change
AI local volumesbin/backup.sh (above)Daily