Orca IDE server setup
Orca as a persistent server
Target: orca-ide runs as a systemd user service on this machine (the Linux
desktop you SSH into), so terminals/agents keep running after you close the
desktop app or the SSH session, and the server comes back after a reboot.
Written 2026-10-25 against Orca 1.4.238.
The two things that make this non-obvious
1. The CLI defaults to the wrong profile. There are two profiles on this box:
| Path | What it is |
|---|---|
~/.orca |
the live one — paired devices, agent-session journal, terminal history, chat scratch (~18 MB) |
~/.config/orca |
Electron's default profile; created 25/10 07:15, contains only a 155 MB orcad-artifacts download |
orca-ide status reports "not running" while the server is running, because it
looks in ~/.config/orca. Every command below therefore pins the profile:
export ORCA_USER_DATA_PATH=/home/USERNAME/.orca
Add that line to ~/.bashrc if you want it permanent.
2. /usr/bin/orca is the GNOME screen reader, not Orca IDE.
~/.local/bin/orca shadows it today, but the ordering is fragile. Always use
orca-ide (→ /home/USERNAME/.local/bin/orca-ide → /opt/Orca/resources/bin/orca-ide).
Good news: loginctl show-user USERNAME already reports Linger=yes, so user
services survive logout and start at boot.
1. Look before touching
export ORCA_USER_DATA_PATH=/home/USERNAME/.orca
orca-ide status # expect: appRunning: true, pid <orcad pid>
ps aux | grep -i orca | grep -v grep
ss -tlnp | grep -E '6768|44267'
systemctl --user list-units --all | grep -i orca
What was running at the time of writing: orcad (pid 54008) serving
127.0.0.1:6768, its Electron web-UI child on 0.0.0.0:44267, and two terminal
daemons in orca-daemon-*.scope. Critically, the runtime lived in
session-9.scope — an SSH login already in state closing, whose
RemoteHost was 100.64.0.1. That is why it was doomed: not supervised, not
enabled, and killed when systemd reaps the session.
2. Stop the old stack
orca serve refuses to start (exit code 3) while another Orca serves the same
profile, so this comes first.
systemctl --user stop 'app-orca-*.scope'
systemctl --user stop 'orca-daemon-*.scope'
pgrep -f 'orcad\.js --json' | head -1 | xargs -r kill # the headless runtime wrapper
pkill -f 'orca-ide --serve' || true # web UI, if still alive
Verify both ports are free:
ss -tlnp | grep -E '6768|44267' || echo "ports free"
Notes:
- Any terminals/agents inside those daemons stop here. Do this before starting work.
- Only run this before installing the service — afterwards it would kill
your new server too (the service unit is
orca-serve-*.service, butorca-daemon-*.scopeis shared).
3. Pair your devices (do this before installing)
Why a foreground run instead of the service: the installer hardcodes
--no-pairing, because a pairing offer is a live credential and a service's
output lands in the journal. One offer can only be minted by a foreground
server (or by a desktop app already attached to this runtime).
One offer enrols one device. Offers are short-lived (relay invites expire in at most 10 minutes) — treat them as single-use.
# still no service running, still ORCA_USER_DATA_PATH set
orca-ide serve
It prints the bound endpoint, the advertised endpoint, and a pairing link
(orca://pair?code=...) plus a browser URL. Pair the device now, then, for each
additional device:
Ctrl-C, then: orca-ide serve # fresh offer for the next device
For a phone, use a mobile-scoped offer instead:
orca-ide serve --mobile-pairing # prints a QR / mobile link
When done pairing, Ctrl-C. Pairing persists: the offer carries this profile's
E2EE host public key (~/.orca/orca-e2ee-keypair.json), and clients pin that
key, so restarts and reboots do not require re-pairing.
Check who is paired at any time (read-only, no tokens printed):
python3 -c "
import json
for d in json.load(open('/home/USERNAME/.orca/orca-devices.json')):
print(d['scope'].ljust(8), d['name'].ljust(45), d['deviceId'])
"
4. Install the service
orca-ide serve service install --dry-run # prints the unit, writes nothing
orca-ide serve service install
The install writes this unit, then runs daemon-reload, enable and
restart — i.e. the server starts immediately.
~/.config/systemd/USERNAME/orca-serve-3eba5288.service
(the 3eba5288 suffix is derived from ORCA_USER_DATA_PATH; the default
profile would give plain orca-serve.service)
[Unit]
Description=Orca server (orca serve --supervise)
StartLimitIntervalSec=900
StartLimitBurst=5
[Service]
Type=simple
ExecStart=/home/USERNAME/.local/bin/orca-ide serve --supervise --no-pairing
Environment=ORCA_USER_DATA_PATH=/home/USERNAME/.orca
Restart=on-failure
RestartSec=10
TimeoutStopSec=90
KillMode=process
[Install]
WantedBy=default.target
Why the flags matter:
--supervise— restarts with backoff, waits (instead of dying) while the app updates or another Orca serves the profile, and gives up cleanly after 6 failures in 15 minutes.--no-pairing— no live offer in the journal. Pairing is a manual step (§3).KillMode=process— stopping the service stops the server but leaves the terminal daemon and your terminals alive.
5. Verify
orca-ide serve service status
systemctl --user status 'orca-serve-*.service' --no-pager
journalctl --user -u 'orca-serve-*.service' -n 50 --no-pager
ss -tlnp | grep 6768
orca-ide status
Expect: unit enabled + active (running), runtimeState: ready,
runtimeReachable: true. There will be no pairing link in the log — by design.
6. Reboot survival
| Requirement | Status |
|---|---|
loginctl enable-linger USERNAME |
already yes (persists across boots) |
unit enabled in default.target |
done by the installer |
loginctl show-user USERNAME --property=Linger --value
systemctl --user is-enabled 'orca-serve-*.service'
With lingering on, the user manager starts at boot without anyone logging in, so the server comes up on its own. Verify for real with a reboot, then re-run the checks in §5.
What survives vs. what doesn't:
- Survives: the server, pairings, terminal journal, orchestration DB.
- Does not survive: live processes inside terminals. Orca restores sessions from
its journal, but large restores are throttled — agents may come back asleep
and resume when you open them.
orca-ide serve restore resumeclears a paused restore.
Reboot gotchas:
- Give-up brake is on disk at
~/.orca/serve-supervisor-brake.json, keyed by build. Once the supervisor gives up, a reboot will not retry — the unit exits 0 immediately. Fix the cause, re-runorca-ide serve service install(that clears the brake), ororca-ide serve --supervise --forceonce outside the service.serve service statusreportsgaveUp. - systemd's own limit (
5starts /900s):systemctl --user reset-failedbefore it will try again. - The unit has no
After=network-online.target. Fine today (binds loopback, no pairing offer). Add ordering if you ever depend on Tailscale being up at boot.
Multiple devices
Yes — multiple devices can be paired, and this profile already has five:
| Scope | Name |
|---|---|
| runtime | CLI 21/10/2026 |
| runtime | CLI 22/10/2026 |
| runtime | CLI 25/10/2026 |
| mobile | Mobile 21/10/2026 via machine-name |
| mobile | Mobile 21/10/2026 via machine-name |
Details worth knowing:
- Each device gets its own
deviceIdand token, tracked in~/.orca/orca-devices.json. No device cap is enforced in this build. runtimescope = the desktop/CLI client;mobilescope = phone. A mobile device records theparentDeviceIdof the runtime device that invited it.- Offsets of the scale: offers are single-device. Pair each device from its own
foreground
orca-ide serverun (or--mobile-pairingfor a phone), or mint more offers from a desktop app that is already paired. - Manage/revoke from the desktop app's device list. Hand-editing
orca-devices.jsononly takes effect with the server stopped, and is easy to get wrong — prefer the app.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
serve exits 3 |
another Orca serves this profile | stop it (§2), check ss -tlnp | grep 6768 |
orca-ide status says not running but the service is up |
wrong profile | export ORCA_USER_DATA_PATH=/home/USERNAME/.orca |
| service active, then immediately inactive | supervisor gave up | orca-ide serve service status; re-run serve service install |
start request repeated too quickly |
systemd start limit | systemctl --user reset-failed 'orca-serve-*.service' |
| pairing link rejected / expired | offer is short-lived and single-use | re-run orca-ide serve, mint a fresh one |
| port 6768 already in use | stale server or another profile | ss -tlnp | grep 6768, stop the owner |
| desktop app shows an empty workspace | it opened on ~/.config/orca |
launch with ORCA_USER_DATA_PATH=/home/USERNAME/.orca orca-ide |
Rollback
orca-ide serve service uninstall # stops it, removes the unit; daemon + terminals stay
Then, if you want the old headless runtime back, start orcad manually (it is
not supervised and will not survive a reboot).
Quick reference
export ORCA_USER_DATA_PATH=/home/USERNAME/.orca
orca-ide serve # foreground server, prints pairing link
orca-ide serve --mobile-pairing # phone QR / link
orca-ide serve service install # install + enable + start the unit
orca-ide serve service status # is it installed / live / gave up?
orca-ide serve service uninstall # stop + remove
orca-ide serve restore resume # un-pause throttled session restores
orca-ide status # runtime reachability for this profile