Installation
Procedure to install PlantPulse Studio on a single customer server. It consists of four Docker containers (web, server, builder sidecar, and PostgreSQL), and the installation script automatically handles everything from Docker setup to startup.
If you are on an air-gapped network without internet access, see air-gap installation instead of this document.
1. Prerequisites
Server specifications
| Item | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 4 cores | 8 cores or more | Build runs per app session |
| Memory | 8 GB | 16 GB or more | See table below |
| Disk | 50 GB free | 100 GB or more | Container images alone ~6 GB |
| OS | Linux (x86_64) | RHEL 9 / Rocky 9 / Ubuntu 22.04 or later | Just needs systemd + Docker |
Memory calculation basis — fixed upper limits held by the stack.
| Container | Memory limit |
|---|---|
| studio-server (orchestrator) | 2 GB |
| agent-server (builder sidecar) | 2 GB |
| studio-web (nginx) | 256 MB |
| postgres (bundled mode) | Unlimited (actual use: hundreds of MB) |
| Per app session container | 2 GB |
App session containers start one per user opening a project and are automatically reclaimed after 30 minutes of non-use. Add 2 GB for each concurrent worker.
Plan on 16 GB memory · 8 cores · 100 GB disk as a comfortable baseline.
Software
- Docker Engine + Docker Compose v2 (v2.22 or later) If not installed, the one-line installation script will install it automatically.
curl,tar(included by default in most distributions)
Ports and firewall
The stack runs on host network. Container ports are bound directly to the host, so access control is solely through server firewall rules.
| Port | Purpose | Firewall |
|---|---|---|
| 80 | Studio UI (nginx) | Open |
| 5171 | App listener — preview · deployed app · QR access | Open (required) |
| 5170 | Studio API | Web acts as internal proxy — no need to open (diagnostic) |
| 5432 | PostgreSQL (bundled mode) | Binds to 127.0.0.1 only — do not open |
| 8000 | Builder sidecar (agent-server) | Loopback only — never open |
| 5172 | MCP tool bridge | Loopback only — never open |
Preview (/preview/…) and deployed apps (/apps/…) are served from a different origin (default port 5171) for security reasons. This prevents chat-generated app JavaScript from accessing Studio login tokens. If 5171 is not open, screens will appear blank.
These ports are for internal component communication only and have no authentication. If exposed externally, anyone can read and write all user workspaces. The stack binds to loopback by default, but never open these in the firewall either.
Registry access
Images come from the KOPENS registry (docker.kopens.io). Log in once before installation.
Credentials are issued by the KOPENS operations team.
docker login docker.kopens.io
2. Database selection
Choose one before installation. You can change it later, but data migration will be required.
=== "(A) Bundled PostgreSQL — default"
The stack will bring up a PostgreSQL container alongside. This is the default for sites without a separate database server.
COMPOSE_PROFILES=bundled-pg
PG_DB=ppstudio
PG_USER=ppstudio
PG_PASSWORD=`<long-random-string>`
Data is stored under DATA_ROOT/postgres.
=== "(B) External or shared PostgreSQL"
If you already have PostgreSQL for the Platform, you can eliminate dual infrastructure.
Delete the COMPOSE_PROFILES line and specify only the connection URL; the PostgreSQL container will not start at all.
# Delete COMPOSE_PROFILES line
DATABASE_URL=postgres://`<user>`:`<password>`@`<db-host>`:5432/`<db-name>`
Just prepare an empty database — the server will automatically create tables on first startup.
In bundled mode, if you change only PG_PASSWORD after data has accumulated, the existing data directory's account will mismatch and the stack won't start. To change the password, you must first change the account password within PostgreSQL.
3. Installation (one-line · recommended)
Log in as root on the server and run one line. The script will check/install Docker → download packages → generate .env → start the stack.
curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo bash
Installation location is /opt/kopens/plantpulse-studio-docker, and all subsequent operational commands run from this directory.
Pre-set values for unattended installation
Environment variables must be placed before bash to be passed through the pipe.
curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh \
| sudo PLATFORM_API_TARGET=https://192.168.0.41 \
DATA_ROOT=/data1/pp-studio \
ANTHROPIC_API_KEY=sk-ant-... \
bash
Prompt for values
curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo INTERACTIVE=1 bash
Each blank field will prompt (data root · PG password · Platform address · admin account, etc.).
Re-running the installation script on the same server will update code only; .env and local backups (dist/) are preserved. You can use it as-is for upgrades.
CDN cache may be lingering. Append ?v=$(date +%s) to the URL and download again.
4. Manual installation
Use this if you already have the package or want to write .env directly.
cd /opt/kopens/plantpulse-studio-docker
sudo cp .env.example /etc/kopens/plantpulse-studio.env
sudo vi /etc/kopens/plantpulse-studio.env # 아래 표를 보고 값 채우기
sudo chmod 600 /etc/kopens/plantpulse-studio.env
bash bin/start.sh
/etc/kopens/plantpulse-studio.env aloneThe environment variable file goes outside the repo tree in /etc/kopens/plantpulse-studio.env, not inside the installation directory. Permissions are 0600. All three products (platform, ai, studio) follow the same convention for /etc/kopens/plantpulse-<product>.env.
The .env at the installation directory root is the old path. Compose and other scripts no longer read that file, so editing it won't change the stack. For existing installations, .env moves bin/install.sh to the new path (it does not create a new one — creating new would leave the DB with the old password but env with new defaults, causing failure on next restart).
Key environment variable items
| Item | Description | Default |
|---|---|---|
REGISTRY | Image registry. Leave empty to use local images only without pull (air-gap) | docker.kopens.io/ps |
TAG | Image tag. Pin to specific version like 2026.08 | latest |
DATA_ROOT | Path where all data (workspaces · builds · state · DB) are stored | /var/lib/pp-studio |
PLATFORM_API_TARGET | PlantPulse Platform address for delegating real data and authentication | https://192.168.0.41 |
APPS_PORT | App listener port (preview · deployed app) | 5171 |
PP_LANG | Web default language ko / en | en |
PP_TZ | Timezone (IANA ID). Sets "today · yesterday · last week" interpretation | Asia/Seoul |
STUDIO_CORS_ORIGINS | Specify only when calling API from different domain. Default: blocked | empty |
COMPOSE_PROFILES / DATABASE_URL | Database selection — see section 2 above | bundled-pg |
ANTHROPIC_API_KEY etc. | AI · Platform keys — see secrets management | empty |
STUDIO_LOCAL_USERS · STUDIO_ADMINS · STUDIO_VIEWERS no longer exist (2026-08-18).
When the server starts with no accounts, it seeds a bootstrap admin into the DB. Afterwards, accounts are added/removed and roles (admin / builder / viewer) are assigned via the app's Settings → User Management.
See First login below.
PP_LANG=ko directlyThe .env created by one-line installation lacks PP_LANG and PP_TZ lines, so defaults (en / Asia/Seoul) apply. To start in Korean, add PP_LANG=ko to .env and bash bin/restart.sh.
.env, always run bin/restart.shdocker restart does not re-read .env. Values won't reflect, and you'll spend time wondering "why didn't it change?" — a classic pitfall. Details are in secrets management.
5. Stack composition check
After installation, four containers should be running.
| Container | Role |
|---|---|
pp-studio-web | Studio UI (nginx :80) — static screens + API proxy |
pp-studio-server | Orchestrator (:5170) — agents · sessions · deployments · app listener (:5171) |
pp-studio-agent-server | Builder sidecar — coding engine for app code |
pp-studio-postgres | Bundled PostgreSQL (does not start if using external DB) |
Additionally, when a user opens a project, an app session container starts — one per project. These are not stack containers but are created by the server on demand and reclaimed after 30 minutes of idle time.
6. Startup verification
cd /opt/kopens/plantpulse-studio-docker
bash bin/status.sh # 컨테이너 상태 + 헬스 + 세션 컨테이너 수
curl -s localhost:5170/health
Right after installation, run a comprehensive health check script once. It inspects health · web response · actual login · session runtime image · container status, and exits with error if anything fails.
bash bin/smoke-install.sh
✓ health
✓ client-config → {"lang":"ko",...}
✓ web 200
✓ 로그인(부트스트랩 계정)
✓ 세션 런타임 이미지
✓ 컨테이너 running(...)
✅ 설치 스모크 통과
App session containers use the plantpulse-studio-runtime image. bin/start.sh automatically downloads and tags it, but if it fails, a ⚠️ 세션 런타임 이미지 없음 warning appears and opening projects will fail. Check registry login status and re-run bash bin/start.sh.
7. First login
Open http://<server-ip>/ in your browser.
When the server starts with no accounts, it seeds a bootstrap admin into the DB.
| Item | Value |
|---|---|
| Username | admin |
| Password | admin123! |
It also appears in boot logs — 부트스트랩 관리자 생성: admin — 로그인 후 비밀번호를 바꿀 것.
It is the same value on every installation. Log in and immediately go to Settings → User Management to change it (password reset, 6 characters or more). Editing the environment variable file won't change it — the server checks ① DB → ② environment variables on login, and stops at the DB if found.
After login, do these:
- Settings → User Management — change bootstrap admin password, create field user accounts, assign roles (admin / builder / viewer)
- Change password · API keys — rotate DB account and AI keys in one go via
bin/passwd.sh. Only do this once during commissioning - Settings → AI — verify AI provider and key connection status
- Settings → Platform — verify Platform address and key connection (required for real data queries)
Detailed screen explanations are in Getting started and Settings (admin).
8. Daily operations commands
All commands run from the installation directory (/opt/kopens/plantpulse-studio-docker).
bash bin/start.sh # 시작(레지스트리에서 최신 이미지 pull 포함)
bash bin/stop.sh # 중지 — 데이터는 그대로 보존, 세션 컨테이너도 정리
bash bin/restart.sh # 재기동(.env 변경 반영)
bash bin/status.sh # 상태 요약
bash bin/logs.sh # 서버 로그 팔로우(기본 studio-server)
bash bin/logs.sh studio-web # 특정 서비스 로그
To see session containers only,
docker ps --filter label=plantpulse-studio=1
9. Updates
bin/start.sh pulls fresh images from the registry. Re-running the installation script also updates operational scripts (.env · backups are preserved).
=== "Images only"
cd /opt/kopens/plantpulse-studio-docker
bash bin/start.sh
=== "Package and images"
curl -fsSL https://product.kopens.io/plantpulse-studio/install.sh | sudo bash
Pin version
Version notation is YYYY.MM format (e.g., 2026.08). In production environments where auto-update is not wanted, pin the tag in .env.
TAG=2026.08
Run bash bin/backup.sh first. The procedure is in backup and recovery.
Image signature verification (optional)
Verify that deployed images are signed by KOPENS. Requires cosign to be installed.
cd /opt/kopens/plantpulse-studio-docker
bash bin/verify-images.sh
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-server:2026.08
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-web:2026.08
[verify] ✅ docker.kopens.io/ps/plantpulse-studio-runtime:2026.08
Next steps
- Secrets management — safely store AI and Platform keys
- Domain and reverse proxy — serve via domain and HTTPS instead of IP
- Backup and recovery — install auto-backup and recovery rehearsal
- Troubleshooting — symptom-to-cause-and-remedy table