Skip to main content

Installation

Three ways to run Orkestra, in ascending order of control: the TUI, the CLI, and raw Compose. They all drive the same compose files and the same docker/.env.

Prerequisites​

  • Docker 24+ with Compose v2
  • A docker/.env file — copy docker/.env.example, or let the setup wizard write one
  • Network reachability to GHCR (ghcr.io) if you're pulling the pre-built backend image

For building from source rather than pulling, install the pinned toolchains with mise: mise install reads .mise.toml and provisions exactly the Go, Node, Flutter, and golangci-lint versions CI uses — local and CI cannot drift.

1. The TUI​

./orkestra.sh

Four options: Full stack (dev, staging, or production — the environment is autodetected from docker/.env), Observability (the self-hosted Loki/Tempo/Prometheus/Grafana overlay, which runs alongside any app stack), Setup (the guided docker/.env wizard), and Quit.

Start with Setup on a fresh clone — it scaffolds secrets and JWT keys for you — then Full stack → deploy.

2. The CLI​

The same script is non-interactive when given a command, which is what you want in scripts and over SSH:

./orkestra.sh init --yes # scaffold docker/.env, secrets, JWT keys — no prompts
./orkestra.sh deploy --scope backend --yes # all | backend | frontend-admin | frontend-admin+backend | infra
./orkestra.sh status # containers + health + resources
./orkestra.sh logs backend # follow one service
./orkestra.sh stop --with-infra # stop the app services, and infra too

Prefer this over raw docker compose: the script injects the version stamp, runs preflight, and resolves the right compose file from ENV, none of which happens if you call Compose yourself.

3. Raw Compose​

cd docker
docker network create orkestra-network # first time only
docker compose -f docker-compose.infra.yml up -d # mongodb + redis + rustfs
docker compose -f docker-compose.dev.yml --env-file .env up -d

One infra base plus one app file per environment — docker-compose.{dev,staging,prod}.yml — and an opt-in observability overlay. The dev backend builds with AIR pre-baked, so a source edit rebuilds in place.

Always check which environment your .env selects before running a Compose command:

grep '^ENV=' docker/.env

Staging uses docker-compose.staging.yml, dev uses docker-compose.dev.yml. Pointing the wrong file at a live stack is the classic way to half-recreate it.

Pre-built image vs. build from source​

There is a single backend image — no SKUs, no per-vertical builds:

ImageContents
ghcr.io/orkestra-cc/orkestra/backend:latestThe eight core modules
ghcr.io/orkestra-cc/orkestra/backend:<sha>The same, pinned to a commit

Pull it to evaluate. Build from source when you're developing, or when your fork has added its own modules — a fork's addons live in-tree and produce their own image.

First login​

On every environment — local development included — open the operator console: it redirects to the setup wizard, which creates the first administrator and the initial Tier-1 organization for you. See First admin login.

On a local dev stack you can additionally mint a token for calling the API with curl (the endpoint doesn't exist on staging or production):

ORKESTRA_API_URL=http://localhost:3000 ./scripts/devtoken.sh administrator

:::warning localhost assumes HOST_BIND_ADDRESS=0.0.0.0 That is the default and what docker/.env.example ships, but the compose files publish the browser-facing ports on that address only. A host that pins it to one interface — a VM serving the stack over a private-network IP, for instance — is reachable only there.

The failure is easy to misread: curl reports a connection refusal, and while devtoken.sh does exit non-zero, the common T=$(./scripts/devtoken.sh …) idiom discards that status, leaving you with an empty token and a puzzling 401.

Substitute the real address for localhost, or read it back:

sed -n 's/^HOST_BIND_ADDRESS=//p' docker/.env

:::

Where things listen​

ServiceURL
Backend APIhttp://localhost:3000
Interactive API docshttp://localhost:3000/docs
Operator consolehttp://localhost:8080

Open the console on localhost, not on console.localhost. Its SPA calls whatever VITE_API_URL says — http://localhost:3000 by default — with credentialed requests, and a refresh cookie is SameSite=Lax, so the console's own origin and VITE_API_URL must be the same site: both localhost (the shipped pairing above), or both console.localhost (which also needs VITE_API_URL=http://console.localhost:3000 and console.localhost in VITE_ADMIN_ALLOWED_HOSTS, since the console dev server does not allow that hostname by default). One of each silently breaks session refresh — see Refresh + cookie scope. ./orkestra.sh deploy refuses to start a stack whose FRONTEND_URL and VITE_API_URL name different sites — under *.localhost, different hosts.

The operator and client surfaces are separated by Host header, not by port — see the audience model. In development an unrecognised Host falls through to the operator mux, so plain localhost keeps working without editing /etc/hosts; in staging and production it returns 421 Misdirected Request.

No mail on a fresh install​

The notification module boots in noop mode: verification and password-reset emails are written to the backend's stdout instead of being delivered. That is deliberate, and it means a fresh install needs no SMTP credentials to be usable. See Notifications & SMTP when you want real delivery.