Reference

Files and HTTP API

Besides orca.toml, morca reads and writes an HTTP API (every UI page is also JSON), logs and machine metrics under ORCA_LOGS, an orca schema in Postgres, and its environment.

The HTTP API

orca ui serves every page as HTML, and as JSON to Accept: application/json or ?format=json; paths under /api/ are always JSON. Reading needs no login. An action needs a session cookie from a browser or an API token from a script (Authorization), and is audited as its user's. A worker's token works only on /api/worker/, and a user's token never does. The full route table is the docstring of routes.mojo, and ui.md describes each page and its JSON fields.

Pages and their JSON

PathWhat it answers
/the pipelines with their coverage, schedules, next and last runs
/pipelines/<name>a pipeline's tabs (?tab=overview, runs or operate): steps, partitions, runs, fill, waiting runs and its registration (config)
/api/pipelines/<name>the pipeline page's JSON, whatever is asked for
/pipelines/<name>/progressthe Progress card as a fragment: runs going, fill, how far a sensor is behind, coverage
/pipelines/<name>/calendara daily or monthly pipeline's days or months of ?month= with their state, as JSON
/pipelines/<name>/keys/<key>one key of a keyed pipeline: its steps in every run it was in
/runsruns newest first, filtered by ?pipeline=, ?status=, ?trigger=, ?from=, ?to=, ?key=, paged with ?before= and ?after=
/runs/<id>a run's cards, steps, machines and the snapshots each step read and wrote
/runs/<id>/attempts/<n>an attempt's log and error; /log is the log as a fragment the page refreshes while it runs
/runs/<id>/timeline.jsonthe run's Dista timeline, in Chrome's trace format
/runs/<id>/driver.logthe run's driver output, as the scheduler kept it
/runs/<id>/cluster/<name>an EC2 run instance's head or worker log, for the run's own instances only
/runs/<id>/metricsthe run's machines: charts and peaks from its samples
/api/ganttthe pipelines and their runs over ?window=1h, 6h, 24h or 7d, for the home page's Timeline
/datasets, /datasets/<name>datasets with their writers, readers and snapshots, and one dataset's place in the graph
/datasets/<name>/snapshots/<id>a snapshot's lineage: back (what it was made from) and forward (what was made from it)
/rebuilds/<id>a rebuild's pipelines by level, with their runs
/ops/alerts, /api/alertsevery alert sent, newest first, with its delivery and whether it's still open
/ops/control, /api/systemthe control host's CPU, memory, disk, network, containers' memory and memory pressure
/ops/pools, /api/poolsevery machine runs are placed on: EC2 instances, Kubernetes pods and workers; from 0.24.0 also autoscaled
/ops/pools/<kind>/<id>one machine: its facts, an instance's samples or the runs a worker claimed
/api/fleet, /api/workersFleet's summary, hosts and charts, and each worker token's machine with its heartbeat and runs
/watchdog, /api/watchdogthe watchdog's rules with their values, limits and snoozes, and its open trips
/metricsPrometheus gauges: the control host, runs running and queued, hosts, memory pressure and recent errors

Pages that refresh have a fragment twin (/runs/table, /home/pipelines, /ops/pools/live, /ops/control/live and others) that the page polls. The old /fleet, /ops/fleet, /ops/workers and /system redirect to Pools and Control.

A run's cluster (from 0.24.0)

/runs/<id>'s JSON gains cluster: the workers a run's Dista cluster spread over, when a pipeline placed on a worker pool has workers = N. The first row is the worker that started the run, holding its head; each later one a member that joined. It is an empty list for a run on one machine. The run page shows the same as its Cluster card.

"cluster": [
  {"role": "head", "start": 1, "worker": "orca-pool-lab-3", "address": "10.77.0.3:41237",
   "grant": {"cpus": 8, "memory_mb": 15360}, "joined": "2026-11-11T09:12:04Z",
   "ended": null, "why": null},
  {"role": "member", "start": 1, "worker": "orca-pool-lab-4", "address": null,
   "grant": {"cpus": 8, "memory_mb": 15360}, "joined": "2026-11-11T09:12:31Z",
   "ended": "2026-11-11T09:40:10Z", "why": "its run ended"}
]

Autoscaled pools (from 0.24.0)

/ops/pools' JSON and /api/pools gain autoscaled: each [pools.<name>] morca grows, with its types and waits (null for none), locations, limits, live machines, the type it is trying and what its last try came to. Pool machines are named orca-pool-<pool>-<n>.

"autoscaled": [
  {"name": "lab", "provider": "hetzner",
   "machines": [{"type": "cax31", "wait": 600}, {"type": "cpx32", "wait": null}],
   "locations": ["hel1", "nbg1"], "max_machines": 3, "idle_seconds": 900,
   "live": 2, "trying": null, "status": null}
]

Actions

Each is a POST of a form, or of JSON from orca --remote. It answers 303 See Other to the page of what it made (a script gets that page's JSON with 200), or 409 saying why it wasn't done.

POSTWhat it does
/pipelines/<name>/runsstart a run over partitions
/pipelines/<name>/backfillqueue a run per partition that has none; rerun=1 for done ones too
/pipelines/<name>/cancelcancel its queued, running and retrying runs over partitions
/pipelines/<name>/pause, /unpausestop the scheduler starting its runs, or let it again
/runs/<id>/resumequeue a failed, cancelled or stalled run again; allow_new_code=1 confirms new code
/runs/<id>/rerunmake a new run from step on, with the steps before it copied
/runs/<id>/cancelcancel a run before its next attempt
/runs/bulkcancel, retry or backfill the runs picked from a list, or all its filters match
/datasets/<name>/rebuildrebuild every pipeline downstream over partitions
/watchdog/<rule>/snoozesilence a watchdog rule for duration (2h, 30m, 1d; off ends it)
/watchdog/<trip>/ack, /watchdog/all/ackacknowledge a watchdog trip, or all of them

Logging in

PathWhat it does
/loginGET is the form; POST name, password and next opens a session in the orca_session cookie
/logoutends the session and clears the cookie
/api/loginJSON name, password, label and days gives an API token, for orca login
/api/logoutrevokes the API token it's sent with

The worker API

What orca worker and the runs it starts call: each a POST of JSON with Authorization: Bearer <worker token>. The token must be a live worker token (else 401), the body a JSON object (400), and a pool it names the token's (403). With ORCA_WORKER_ADDR set, the UI also serves these, and only these, on that address from a thread of their own, so a slow page can't hold up claims and heartbeats. See Remote workers and api/worker.mojo.

POSTWhat it does
/api/worker/claimthe worker's heartbeat; with slots above 0 it also asks for a queued run of its pool, answered with the run's spec or null
/api/worker/releasehands back a run it was given and won't start, which is queued again and held saying why
/api/worker/exiteda run's process ended, with its samples
/api/worker/clusterreports the dista head's address and token of a run this worker started as a cluster
/api/worker/lefta cluster member's process ended
/api/worker/boota pool machine booting gets the image, the login to pull it and the bucket's variables
/api/worker/store/<call>a run's store calls (its steps, attempts, reads, commits and heartbeat), in place of Postgres

Logs on disk and in object storage

With ORCA_LOGS set to a directory or s3://bucket/prefix, everything a run leaves is under <ORCA_LOGS>/<pipeline>/<run>/. Without it, logs go to standard output only. An object can't be appended to, so a log bound for object storage is written under ORCA_LOG_STAGE and uploaded when it's complete. Secrets named in ORCA_SECRETS are masked as *** in every log morca writes, and each attempt's row records where its log ended up.

Path under ORCA_LOGSWhat it holds
<pipeline>/<run>/<step>/<partition>/<attempt>.logan attempt's start, end, error and ctx.log lines with UTC timestamps; the partition is _run for a gather step, and a key's / is %2F
<pipeline>/<run>/timeline.jsona run with Dista tasks: the runtime's timeline in Chrome's trace format, written when the run ends
<pipeline>/<run>/driver.logthe run's driver output, kept by the scheduler, or by the driver pod's log on Kubernetes
<pipeline>/<run>/cluster/<role>-<instance>.logan EC2 run's head or worker log, uploaded every ORCA_CLUSTER_LOG_SECONDS while it runs
<pipeline>/<run>/metrics/local.jsonlthe machine's samples for a run on the local runner
<pipeline>/<run>/metrics/<role>-<instance>.jsonleach EC2 run instance's samples
<pipeline>/<run>/metrics/<pod>.jsonleach Kubernetes pod's samples, taken out of its log
<pipeline>/<run>/metrics/worker-<name>.jsonla remote worker's samples beside each run it starts

A control-placed run's driver output is staged under ORCA_LOG_STAGE/control/ before it's appended to driver.log. On a remote worker, the logs of a cluster's head and members stay in the worker's --dir.

Metrics lines

<binary> sample runs beside every process doing a run's work and appends a JSON line every ORCA_SAMPLE_SECONDS (5). Readers ignore keys they don't know, since fields may be added. The run page's Machines section draws these files.

{"v": 1, "t": 1762250000.123, "dt": 5.0, "host": "ip-10-0-1-5", "cores": 4,
 "cpu": {"user": 61.2, "system": 4.1, "iowait": 0.3, "steal": 0.0, "busiest": 99.0},
 "mem": {"total": 8053063680, "used": 3221225472, "available": 4831838208,
         "cache": 2147483648, "swap_total": 0, "swap_used": 0, "rss": 2952790016},
 "disk": [{"path": "/tmp", "total": 53687091200, "used": 9663676416, "free": 44023414784}],
 "io": {"read": 1048576, "write": 52428800}, "net": {"in": 1310720, "out": 655360},
 "self_cpu": 0.42}
FieldMeaning
vthe format's version, 1; it changes only if a field's meaning does
t, dtthe Unix time, and the seconds since the previous line
host, coresthe machine's name and its core count
cpuuser, system, iowait and steal as percentages of all cores' time over dt, and busiest, the busiest core's
membytes: total, used, available, cache (page cache), swap_total, swap_used, and rss of the run's own processes
diskper file system the run writes to: path, total, used and free bytes
io, netdisk read and write, and network in and out, in bytes per second over dt; io is null on macOS
containerson the control host only: each Docker container's name and memory
self_cputhe sampler's own CPU seconds so far

The sampler's options and platform differences are in the docstring of sample.mojo.

The Postgres schema

With ORCA_DATABASE_URL set, every orca binary creates and migrates the orca schema at start-up. Each change of a run's state is one transaction. morca 0.23 has 54 migrations, and 0.24.0 adds a 55th (orca.pipelines.fill_max_runs_auto). A sqlite: database holds the core run tables only.

Table (orca.*)What it holds
pipelineseach registered pipeline: its registry hash, describe JSON, config and overrides, env, max_runs, keys_per_run and learned request
pipeline_versionseach code version a pipeline was registered with: hash, commit, dirty flag and image
runseach run: pipeline, partitions, trigger, status, owner and heartbeat, request and grant, spec and cluster head
stepseach step of each partition of a run, with its input, output, status and error
attemptseach attempt of a step: host, code, start and end, error, log location and peaks
reads, commitsthe snapshot of each dataset an attempt read or committed, which lineage walks
firesone row per (pipeline, time) the schedule fired, with its run, so nothing fires twice
sensedeach upstream snapshot a sensor saw, with the run it queued
keysa keyed pipeline's keys, as last read from its dataset's snapshot
rebuilds, rebuild_pipelineseach rebuild of a dataset, and its pipelines by level with their runs
users, sessions, api_tokensUI users, their browser sessions, and the API tokens orca login issues
auditwho did what from the UI or the remote CLI
instanceseach EC2 instance a run used: role, type, market, launch and end, and why it ended
fleet_samples, fleet_hourlymachine samples per host, and hourly totals per role for Ops › Pools' charts
system_minutes, system_containersthe control host's minute-by-minute health, and each container's memory
watchdog_rules, watchdog_tripseach rule's last value, threshold and snooze, and each trip with the pipelines it paused
workerseach worker token: pool, label, expiry or revocation, and the machine's last heartbeat, CPUs, memory and runs
notifications, alertsntfy delivery state per alert subject, and every alert sent, for Ops › Alerts
pressureeach host's latest memory pressure reading and whether it holds runs
schedulerseach scheduler process: host, image, role (leading, waiting, draining) and heartbeat, for overlapping deploys
pools, pool_machinesthe orca.toml pools with what the autoscaler is trying, and each machine it made
stack_identitythis stack's id, the label that marks its pool machines
run_membersthe workers that joined each start of a run spread over a pool, with their grants
schema_versionthe migrations applied, and the fewest an older binary must know to run on them

Additive migrations

A binary older than the database runs on it if every migration it doesn't know is additive, which is what makes deploy.sh --rollback safe. So a migration may add tables, indexes and nullable foreign keys, and columns that are nullable or have a default. It may not rename, drop or retype a column or table, tighten a constraint, or write a value an older binary would act on. A migration that must is listed as raising the floor: orca.schema_version records it, and a binary that doesn't know it refuses to start. Today only migrations 1 and 24 raise it. Queries name the columns they read and write, so new columns never break old binaries. The rules are in migrations.mojo.

Environment variables

Settings for the whole stack are environment variables, in /etc/orca.env on a deployed stack; settings for one pipeline belong in orca.toml's env. Families with many members (ORCA_WATCHDOG_*, ORCA_NOTIFY_*, ORCA_EC2_*, ORCA_POOL_*) are listed in full in watchdog.md, alerts.md, Scheduling and running and orca.toml.

The scheduler

VariableWhat it setsDefault
ORCA_DATABASE_URLthe Postgres database, or sqlite:<path> for one machineunset: in memory
ORCA_RUNNERwhere the scheduler starts runs: local, kubernetes or ec2local
ORCA_LOGS, ORCA_LOG_STAGEwhere logs and metrics go (a directory or s3://), and where object-store logs are stagedunset, $TMPDIR/orca-logs
ORCA_WAREHOUSEthe root of the filesystem catalog, a directory or s3://unset
ORCA_STALE_SECONDS, ORCA_START_SECONDShow old a heartbeat is before a run counts as dead, and the grace before its first beat30; start: 30, 300 on EC2 and Kubernetes
ORCA_RETRY_SECONDS, ORCA_SENSE_SECONDSthe first wait before a retry (doubled each start), and how often sensors are asked60, 60
ORCA_CONFIG, ORCA_PIPELINES_DIRthe orca.toml orca register reads, and where it finds the binaries./orca.toml, pipelines/
ORCA_COMMIT, ORCA_SOURCE, ORCA_DIRTYthe commit an image was built from, and its source, recorded with a registrationunset
ORCA_CONTROL_MAX_RUNS, _MEMORY_MB, _NICE, _WORKERSthe limits of control-placed runs2, 1024, 10, 1
ORCA_HOST_CPUS, ORCA_HOST_MEMORY_MBwhat this host can grant, over what morca readsthe host's
ORCA_RESERVE_CPUS, ORCA_RESERVE_MEMORY_MBkept back from grants1, 1536 (a worker: 0, 512)
ORCA_ADMIT_MEMORY_PRESSURE, ORCA_ADMIT_PRESSURE_GRACE_SECONDSthe PSI limit over which no run starts, and how long a hold lasts with no runs going10, 600
ORCA_CGROUPS0 turns off a cgroup per runon where possible
ORCA_RUN_MEMORY_MB, _MARGIN, _GROWTH, _HELD_SECONDSa run's memory limit without a request, the kill margin, the growth after a kill, and how long it may be held at its limithost's, 25, 1.5, 120
ORCA_RUN_ENVthe scheduler's variables, by name, copied to every rununset
ORCA_VCPU_BUDGET, ORCA_RUN_DEADLINEthe vCPUs live runs' machines may use together, and the seconds after which they're removedno limit, 86400
ORCA_EC2_*head and worker types, AMI, subnet, security group, profile, database URL, preamble, mode and the warm pool's ORCA_EC2_POOL_SECONDSc7g.large, c7g.xlarge, 150
ORCA_SCALE_UP_AFTER, ORCA_SCALE_IDLE, ORCA_SCALE_STEPhow an EC2 run with workers = MIN..MAX adds and drains workers5, 60, 4
ORCA_RUNS_NAMESPACEthe Kubernetes namespace runs' pods go inorca-runs
ORCA_SAMPLE_SECONDS, ORCA_SAMPLE_UPLOAD_SECONDSthe sampler's interval (0 turns it off), and how often an object-store metrics file is uploaded5, 30
ORCA_CLUSTER_LOG_SECONDShow often an EC2 run instance uploads its log30
ORCA_BINARY_SECONDShow long a sensor's binary may take to answer300
ORCA_EXPIRE_HOURShow often every dataset is expired by its own policy; 0 turns it off24
ORCA_OVERLAP, ORCA_DRAIN_SECONDSon lets a new scheduler wait to lead while the old one drains, for at most the drain timeoff, 3600
ORCA_WATCHDOG_SECONDS, ORCA_WATCHDOG_*how often the watchdog checks, and each rule's threshold60
ORCA_NOTIFY_URL, ORCA_NOTIFY_*the ntfy topic alerts go to, its token, and when each alert firesunset: no alerts
ORCA_SECRET_FETCHa command that prints a secret by id, in place of Secrets Managerunset
ORCA_POOL_*, ORCA_HCLOUD_*the pool autoscaler's settings and Hetzner's APIsee orca.toml
ORCA_NOW, ORCA_ZONEINFOa fixed clock for tests, and where time zones are read fromthe clock, /usr/share/zoneinfo

The UI

VariableWhat it setsDefault
ORCA_UI_ADDRhost:port orca ui listens on127.0.0.1:8270
ORCA_WORKER_ADDRa second host:port serving only the worker API, from its own threadunset
ORCA_UI_LOGINrequired makes actions need a login before any user existson once a user exists
ORCA_UI_SESSION_HOURS, ORCA_API_TOKEN_DAYShow long a login lasts, and an API token12, 30
ORCA_UI_SECURE_COOKIES, ORCA_UI_TRUST_PROXY1 marks the cookie Secure, and 1 trusts X-Forwarded-Forunset
ORCA_UI_URLthe UI's public address, for links in alerts and for pool machinesunset
ORCA_UI_LINKSextra nav links after Docs, as label=url entries separated by commasunset
ORCA_UI_DRAIN_SECONDShow long the UI finishes requests after SIGTERM8
ORCA_DOCS_URLwhere the UI's Docs links pointmlake.dev's docs
ORCA_FIXED_COSTon the local runner, what the Cost tile showsunset
ORCA_WORKER_TOKEN_DAYSthe longest a worker token lasts365

Workers and the remote CLI

A worker also reads ORCA_WAREHOUSE, ORCA_LOGS, ORCA_SECRET_FETCH, the reserve and pressure variables, and refuses to start with ORCA_DATABASE_URL set.

VariableWhat it setsDefault
ORCA_REMOTE (or ORCA_URL)the UI a worker, or orca --remote, talks tounset
ORCA_WORKER_TOKENthe worker's token, from orca worker-token createunset
ORCA_WORKER_ADVERTISEthe address the pool's machines reach this worker at, which a run's cluster needsunset: no clusters
ORCA_IMAGEthe worker's version, which must match a pipeline registered with --imageunset
ORCA_CREDENTIALSwhere orca login keeps its API tokens~/.config/orca/credentials

What morca sets for a run

A pipeline's env can't set these; orca does, for the run's process.

VariableWhat it holds
ORCA_RUNthe run's id, for a run a worker or an EC2 instance starts
ORCA_GRANT_CPUS, ORCA_GRANT_MEMORY_MBthe run's grant for this start
ORCA_MEMORY_PRESSURE_FILEthe PSI file its host's pressure is read from
ORCA_SECRETSthe names of the variables holding the run's secrets, which logs mask
ORCA_WORKERSthe local Dista workers the run forks (default 4)
ORCA_STATE_URL, ORCA_STATE_TOKENon a worker, the UI's store API and token the run records itself through, in place of a database
ORCA_WORKER_PIDthe worker that started the run, whose exit stops it
ORCA_DB_RETRY_SECONDShow long a run's database or store calls retry (default ORCA_STALE_SECONDS)

Dista variables morca sets

The runners start a run's Dista head and workers from the pipeline's own binary, telling each what it is with these.

VariableWhat it holds
DISTA_ROLEhead or worker: the binary runs as that Dista process
DISTA_HEADhost:port of the head a Dista worker joins
DISTA_PORTthe port a head listens on: 6390 on Kubernetes, a free port on a worker
DISTA_ADVERTISEthe address a Dista worker is reached at by its head
DISTA_TOKENa run's cluster token, without which Dista refuses a connection; set on a worker pool's clusters
DISTA_WORKER_SLOTSslots per Dista worker; "auto" in env becomes the granted CPUs over the run's workers, else 0, one per core
DISTA_CONNECT_TIMEOUTset to 10 for a cluster member, so it gives up on a head that's gone