Run more than one octo
Everything octo remembers about you lives under ~/.octo: who it thinks you are, what it has
learned, every session, your keys, your skills. One directory, one octo.
A profile gives you another one. --profile work puts that whole set under ~/.octo-work
instead, and the two never see each other.
octo --profile workThat is the entire feature. What makes it worth having is what ends up on each side of the line.
What a profile separates
Section titled “What a profile separates”Each profile gets its own:
- Identity and memory —
soul.md,user.md,octorules.md,memories/ - Sessions —
sessions/, session groups, the trash, input history - Credentials and config —
config.yml(provider, model, endpoint, API key),serve.env - Capabilities —
skills/,workflows/,agents/, and the built-in sets materialized beside them - Connections —
mcp.jsonand its OAuth tokens,channels.ymland IM credentials, the tunnel identity - Governance —
permissions.yml,audit.log, hooks and their trust store - Runtime state — the backend’s pid, logs, uploads, scheduled tasks, Light Apps, browser recordings
Two things stay shared on purpose:
~/.octo/bin— the helper binaries the installers stage there, likeuv. It is machine-level tooling, not your data; a second profile shouldn’t mean a second Python toolchain.- Project-local
.octo/— a repo’s ownhooks.yml, worktrees and Light Apps belong to the repo, and follow it whichever profile you open it from.
A brand-new profile is not empty, either. The skills, workflows and expert agents that ship with the binary materialize into it on first run, exactly as they did for your first profile. What’s empty is the part that was yours: config, memory, sessions, anything you installed yourself.
Naming
Section titled “Naming”Letters, digits, - and _, starting with a letter or digit.
octo --profile team-1 # fineocto --profile "my work" # rejectedocto --profile _lead # rejected — can't start with an underscoreNothing validates that a profile exists, because creating one is just using it. Which also means a typo silently opens a third, empty octo rather than failing, so it is worth checking what you actually have:
ls -d ~/.octo*On the command line
Section titled “On the command line”The flag is global — it works in any position, on any subcommand, in either form:
octo --profile work # interactiveocto --profile=work "summarise this repo" # one-shotocto config --profile work # set that profile's provider and modelocto skills list --profile workThere is also OCTO_PROFILE, which is what makes an alias worth setting up:
alias octow='OCTO_PROFILE=work octo'alias octop='OCTO_PROFILE=home octo'One thing to know about the environment variable: octo passes it down to everything it spawns. A
command the agent runs with the terminal tool inherits it, including a nested octo. That is
usually what you want — a sub-agent stays in the same data root — but it does mean a script run
from inside a work session reads work’s config, not your default one.
The TUI shows no indication of which profile it is in. If you are unsure, octo serve prints it,
or just look at the directory listing above.
Managing profiles
Section titled “Managing profiles”A profile comes into being the first time something runs under its name, so --profile scratch
alone is enough to make one. The management commands exist for everything after that: seeing what
is on disk, making a root ahead of time, and getting rid of one.
$ octo profilesNAME SIZE STATUS PATHdefault 412.3MB current, running (pid 96688) /Users/you/.octohome 18.0MB - /Users/you/.octo-homework 96.5MB running (pid 96701) /Users/you/.octo-work
$ octo profiles create lab # an empty ~/.octo-lab, ready for `octo --profile lab`$ octo profiles path work # /Users/you/.octo-work$ octo profiles rm home --yes # deletes ~/.octo-home and everything in itrm is final — the root holds that profile’s config and API keys, sessions, memory, skills, IM
credentials and logs, and none of it goes through the recycle bin. Without --yes the command
only prints what it would delete. Three roots are refused outright: the default ~/.octo (it also
holds machine-wide state such as ~/.octo/bin), the profile the command itself runs under, and
any profile whose backend is still up — a live pid in its serve.pid, or its pinned address
answering, which is how a foreground octo serve shows up. Stop that one first:
octo serve --profile home stopocto profiles rm home --yesWhat it cannot see is an interactive octo --profile home session in another terminal: a TUI
records nothing under its root. Close those yourself before removing the profile. The name
default is reserved — it is how listings label the unnamed ~/.octo root, and path and rm
accept it as an alias for that root.
The Web UI has the same three operations under Settings → Data → Profiles: the list marks
which root the backend you are looking at runs under and which ones have a live backend, and
deleting asks you to type the profile’s name. It cannot switch profiles — that is a restart of the
backend, which is the desktop tray menu’s job (below) or a new octo serve --profile launch.
Running a backend
Section titled “Running a backend”octo serve is where profiles stop being a private matter, because two backends can’t share a
port.
The default profile keeps 127.0.0.1:8088 — the number every client ships with. A named profile
takes the lowest free port from 8089 up the first time it starts, and then keeps it:
$ octo serve --profile work -docto serve daemon started (pid 96701), ready at http://127.0.0.1:8089
$ octo serve --profile home -docto serve daemon started (pid 96711), ready at http://127.0.0.1:8090The choice is recorded in serve.addr under the profile’s data root and reused verbatim on every
later start. That is the point: an address you typed into a phone, an Obsidian plugin or a VS Code
window is only worth anything if it survives a restart.
Because it is a commitment rather than a preference, a recorded port that turns out to be taken is an error — octo will not quietly move to the next one and strand every client that knows the old number:
$ octo serve --profile workocto serve: profile "work" is pinned to 127.0.0.1:8089, but that address is in use (listen tcp 127.0.0.1:8089: bind: address already in use) pinned by: /Users/you/.octo-work/serve.addr if this profile's own backend is already up: octo serve --profile work status to move this profile somewhere else: octo serve --profile work --addr 127.0.0.1:<port>Free the port, or move the profile deliberately. --addr always wins, and re-records:
octo serve --profile work --addr 127.0.0.1:9100Daemon control is per profile, and status tells you where a profile is listening — for an
automatically chosen port, it is the only place to look:
octo serve --profile work status # octo serve daemon: running (pid 96701) at http://127.0.0.1:8089octo serve --profile work stopUnder a service manager, the unit has to name the profile itself — nothing infers it:
ExecStart=/usr/local/bin/octo serve --profile work --no-supervisorEnvironmentFile=%h/.octo-work/serve.envThe Web UI shows the active profile as a small badge next to the version in the sidebar footer, and
GET /api/version carries a profile field for named profiles. Both are there for the same
reason: with several backends up, the tab in front of you gives no other clue which data it is
looking at.
In the desktop app
Section titled “In the desktop app”Double-clicking an icon passes no arguments, so the desktop app can’t be told which profile to open the way the CLI can. It remembers instead.
Open the tray menu, pick from the Profile submenu, and the app records the choice and restarts into it. The submenu lists the profiles that exist on disk, and only appears once there is more than one. To make that second profile without a terminal, use Settings → Data → Profiles.
One app, one profile. Switching restarts, and a restart loses whatever octo was in the middle of, so it asks first — but only when there is something to lose. An idle backend switches without a dialog. If octo is working on something, or waiting on an answer from you, it says which before going ahead.
Launching from a terminal still works and still wins:
octo-desktop --profile workThat one is deliberately not remembered. A one-off launch shouldn’t redefine what double-clicking the icon opens.
What you’d actually use it for
Section titled “What you’d actually use it for”Work and personal. The clearest case, and the one the identity files are there for. Two
different soul.md files, two sets of memories that never contaminate each other. Under a coding
CLI this would be pointless; for something that is supposed to remember who you are, mixing your
employer’s context with your own is the thing you want to avoid.
Separate keys and endpoints. Your company’s Anthropic key on one side, your own DeepSeek or a local Ollama on the other. No more editing config between runs.
Two IM identities. channels.yml and the IM credentials are single-copy per profile, so before
profiles a machine could only wear one bot identity. Now the company Feishu bot and your personal
Telegram can both be live, on two backends, at two ports.
A strict profile and a loose one. permissions.yml and audit.log are per profile, so one can
run in strict with everything audited — a client environment, production access — while another
runs in auto for your own tinkering.
A throwaway. --profile scratch is a fresh octo: no memory, no config, onboarding from the
top. Good for reproducing someone’s bug, recording a demo, or taking documentation screenshots
without touching your real setup. rm -rf ~/.octo-scratch when you’re done. Cleaner than faking
HOME, since the shared ~/.octo/bin means you don’t reinstall a toolchain to get there.
Limits worth knowing
Section titled “Limits worth knowing”- It is organisation, not security. Same user, same file permissions. A profile keeps a client’s credentials separate; it does not protect them from anything running as you.
- Nothing moves between profiles. A new profile starts empty of your own material. Copying a
skill or an agent across is a
cp. - Nothing searches across profiles. Memory and session search stop at the boundary. That is the trade you are making.
- The desktop app opens one at a time. Two CLI backends can run side by side; two desktop apps cannot.
- There is no
octo profiles list.ls -d ~/.octo*on the command line, the tray submenu in the desktop app.