Skip to main content

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

ItemMinimumRecommendedNotes
CPU4 cores8 cores or moreBuild runs per app session
Memory8 GB16 GB or moreSee table below
Disk50 GB free100 GB or moreContainer images alone ~6 GB
OSLinux (x86_64)RHEL 9 / Rocky 9 / Ubuntu 22.04 or laterJust needs systemd + Docker

Memory calculation basis — fixed upper limits held by the stack.

ContainerMemory 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 container2 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.

If targeting 3–5 concurrent users

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.

PortPurposeFirewall
80Studio UI (nginx)Open
5171App listener — preview · deployed app · QR accessOpen (required)
5170Studio APIWeb acts as internal proxy — no need to open (diagnostic)
5432PostgreSQL (bundled mode)Binds to 127.0.0.1 only — do not open
8000Builder sidecar (agent-server)Loopback only — never open
5172MCP tool bridgeLoopback only — never open
Blocking 5171 breaks preview and deployed apps

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.

Ports 8000 and 5172 have no authentication

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.

Keep PG password consistent from the start

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.


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.).

Safe to run again

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.

If the install script returns 404 or an old version

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
The configuration source of truth is /etc/kopens/plantpulse-studio.env alone

The 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

ItemDescriptionDefault
REGISTRYImage registry. Leave empty to use local images only without pull (air-gap)docker.kopens.io/ps
TAGImage tag. Pin to specific version like 2026.08latest
DATA_ROOTPath where all data (workspaces · builds · state · DB) are stored/var/lib/pp-studio
PLATFORM_API_TARGETPlantPulse Platform address for delegating real data and authenticationhttps://192.168.0.41
APPS_PORTApp listener port (preview · deployed app)5171
PP_LANGWeb default language ko / enen
PP_TZTimezone (IANA ID). Sets "today · yesterday · last week" interpretationAsia/Seoul
STUDIO_CORS_ORIGINSSpecify only when calling API from different domain. Default: blockedempty
COMPOSE_PROFILES / DATABASE_URLDatabase selection — see section 2 abovebundled-pg
ANTHROPIC_API_KEY etc.AI · Platform keys — see secrets managementempty
Accounts and roles are not passed via environment variable

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.

Korean sites must set PP_LANG=ko directly

The .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.

After editing .env, always run bin/restart.sh

docker 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.

ContainerRole
pp-studio-webStudio UI (nginx :80) — static screens + API proxy
pp-studio-serverOrchestrator (:5170) — agents · sessions · deployments · app listener (:5171)
pp-studio-agent-serverBuilder sidecar — coding engine for app code
pp-studio-postgresBundled 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(...)
✅ 설치 스모크 통과
Session runtime image

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.

ItemValue
Usernameadmin
Passwordadmin123!

It also appears in boot logs — 부트스트랩 관리자 생성: admin — 로그인 후 비밀번호를 바꿀 것.

Change this password first

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:

  1. Settings → User Management — change bootstrap admin password, create field user accounts, assign roles (admin / builder / viewer)
  2. Change password · API keys — rotate DB account and AI keys in one go via bin/passwd.sh. Only do this once during commissioning
  3. Settings → AI — verify AI provider and key connection status
  4. 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
Back up before updating

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