Skip to main content

Environment Variable Reference

This is the complete set of environment variables read by the Studio server. Unlike the values you change on the screen (Environment Settings), the values here are applied only after you edit the file and restart.

The canonical file is /etc/kopens/plantpulse-studio.env​

/etc/kopens/plantpulse-studio.env
.env in the repository root is no longer read

Values placed in the old path (.env in the installation directory) will not be applied. If the old file remains, the script will notify you of that fact. When changing values, use the canonical path above.

Set the permissions to chmod 600 — this file contains API keys.


Image and Version​

VariableDefaultDescription
REGISTRYdocker.kopens.io/psRegistry from which to pull images. If blank, only local tags are used (when built directly)
PP_IMAGE_TAG(empty)Calver tag for our image set (server · web)
PS_IMAGE_TAG_SERVER(none)When only plantpulse-studio-server should run with a different tag
PS_IMAGE_TAG_WEB(none)When only plantpulse-studio-web should run with a different tag
PS_IMAGE_TAG_AGENT_SERVER0c5ac7fbAgent sidecar. Not a calver; it uses a digest pin (OpenHands SDK v1.43.1), so it does not follow PP_IMAGE_TAG
Leaving PP_IMAGE_TAG empty is recommended

If left empty, bin/start.sh reads the calver from the VERSION file and automatically tracks each release. If you manually write 'YYYY.MM' here, that value is frozen as-is — in fact, that's why there was an incident with a one-month-old image being deployed.

If you must pin to a specific point in time, use a snapshot tag (2026.09-20260911), not 'YYYY.MM' (2026.09). Do not compute it with date +%Y.%m — between 00:00 and 04:00 KST on the 1st of each month, this points to a tag that does not yet exist (the monthly release is published at 04:00).

There is no such variable as TAG · AGENT_SERVER_TAG

What compose reads are only the names in the table above. If you write something like TAG=2026.08, nothing happens, and the stack runs with the calver from VERSION — you think you've pinned it, but you're actually tracking.

To pin to a specific release, use a snapshot tag — e.g., TAG=2026.08-20260810. :::


Locale and Time Zone​

Uses the same variable names as Platform and Edge.

VariableDefaultDescription
PP_LANGenDefault web language (ko / en). The value stored in the browser takes precedence
PP_TZAsia/SeoulIANA time zone ID. Basis for interpreting relative times like "today/yesterday"

For a Korean field environment, set this to PP_LANG=ko.


Data Storage Location​

VariableDefaultDescription
DATA_ROOT/var/lib/pp-studioWorkspaces, build artifacts, and state are all stored under this path

This path is the backup target → Backup and Recovery.


Database — External PostgreSQL​

Studio uses an external PostgreSQL. You can share the same PG instance that the Platform uses.

VariableExample
DATABASE_URLpostgres://<user>:<password>@<db-host>:5432/ppstudio

🔴 There is no such variable as PG_PASSWORD. The DB password is included in the same line as DATABASE_URL — we consolidated it into one line because a separate variable for just the password could diverge from the URL.

DATABASE_URL is required

If it's empty, the installation halts right there and tells you what's missing — it does not invent a plausible default.

Create the DB and role «before» you run it

The server only runs migrations on an existing database. It does not create the DB or role — bin/provision-platform-db.sh handles that. See "Database Preparation" in Installation.


Platform Integration​

VariableDescription
PP_API_URLPlatform address for real-data queries and delegating login authentication
PP_API_TOKENThe api_key for that platform
Names differ inside the container

In logs or inside the container, they appear as PLATFORM_API_TARGET · PLATFORM_API_KEY. The names to write in .env are PP_API_URL · PP_API_TOKEN — see "The two layers of names" below.


Listener Ports​

VariableDefaultDescription
PORT5170API port for the Studio server (orchestrator). The web proxies it internally, so there's no need to expose it externally — for diagnostics (curl localhost:5170/health)
APPS_PORT5171Separate listener port that serves previews and deployed apps
  • Direct-port access installation — you must also open this port in the firewall for previews, deployed apps, and QR access to work.
  • Behind a reverse proxy — you don't need to open it. The proxy forwards /container /preview · /apps on the same domain to this listener → Domains and Reverse Proxy.
App origin is not specified

The client only uses relative paths. There's no need to separately provide the public URL for previews or deployed apps, so it works as-is even in a configuration that only opens 80/443.


AI Providers​

The values here are initial values. Values entered on the screen (Environment Settings → AI) override these.

VariableDefaultDescription
AI_PROVIDERopenai-compatibleIf blank, it is always openai-compatible
PI_LLM_URL(empty — required)Gateway address. No default is set
AI_MODELgpt-4oModel name
PI_LLM_API_KEY(empty — required)OpenAI-compatible key
PS_ANTHROPIC_API_KEY(empty)Only when using cloud Claude

⚠ If PI_LLM_URL and PI_LLM_API_KEY are empty, the stack refuses to start.

Why no plausible default is set for PI_LLM_URL

If the gateway isn't on that host, the entire chat becomes fetch failed, and because the value looks plausible, locating the cause is expensive. Write the actual address. You can verify whether the entered value is correct via the Connection Test in the Environment Settings → AI tab before saving → Environment Settings (Admin).

Provider is not inferred from the key

The provider is explicitly specified with AI_PROVIDER. If you infer it from which key is present, you can't predict what will appear depending on the env configuration.


CORS​

VariableDefaultDescription
STUDIO_CORS_ORIGINS(empty = same-origin)Specify only when accessing from a separate domain

Installation and Startup Flags​

These are things to prefix before the command, not .env (SKIP_DOCKER=1 bash bin/install.sh).

VariableDefaultDescription
SKIP_DOCKER0If 1, skips the Docker installation step (when it's already installed)
SKIP_WAIT0If 1, the script finishes without waiting for startup to complete
WAIT_READY_MIN10Time (minutes) to wait for startup to complete. Increase on slow connections or large images

Agent Sidecar Resources​

VariableDefaultDescription
AGENT_SERVER_CPUS1Number of CPUs allocated to the builder sidecar
AGENT_SERVER_MEMORY2gMemory limit for the builder sidecar

What is Not Carried via env​

User accounts and roles are not defined via environment variables. When the server finds no accounts, it seeds an admin in the DB; after that, users are added/deleted and roles (admin / builder / viewer) are assigned from the User Management screen of the app.

→ Changing the Initial Password



Two Layers of Names — .env vs. inside the container​

🔴 This is where mistakes are most common. The names seen in logs, docker inspect, and the product source differ from the names to write in .env. You write the left side.

Name to write in .envName visible inside the container
PP_API_URLPLATFORM_API_TARGET
PP_API_TOKENPLATFORM_API_KEY
PI_LLM_URLAI_BASE_URL
PI_LLM_API_KEYAI_API_KEY
PS_ANTHROPIC_API_KEYANTHROPIC_API_KEY

The rest (DATABASE_URL · DATA_ROOT · APPS_PORT · AI_PROVIDER · AI_MODEL · STUDIO_CORS_ORIGINS) use the same name on both sides.

Why the names were split

Multiple products (Platform · AI · Studio) are deployed together on a single server. By adding the product prefix (PP_ Platform · PI_ Common infrastructure · PS_ Studio) to the .env-side names, which product a value belongs to is clear from the name alone, and there's no confusion even when two products share a single value.