{"slug": "show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos", "title": "Show HN: Run multiple Claude Desktop instances side by side on macOS", "summary": "A developer released claude-orchestrator, a macOS command-line and menu-bar tool that runs multiple independent Claude Desktop instances side by side, each with its own profile, account, sessions, Claude Code config and working directory. The tool launches the original signed /Applications/Claude.app with a different data directory rather than copying, patching or re-signing it, so code signature, notarization, permissions and the built-in updater keep working. Version 0.2.0 renamed the tool from claude-ctl to claude-orchestrator while keeping the data directory at ~/Library/Application Support/claude-ctl/ so existing instances keep working.", "body_md": "Run **Claude Desktop** as many independent instances as you want on macOS — each with\nits own profile, account, sessions, Claude Code config and working directory.\n\n`claude-orchestrator` never copies, patches or re-signs `/Applications/Claude.app`. It launches the\n**original, signed** app with a different data directory, so code signature, notarization,\npermissions and the built-in updater all keep working.\n\n```\nclaude-orchestrator new work --workdir ~/workspace/my-project\nclaude-orchestrator start work\nclaude-orchestrator list\n```\n\nOr run `claude-orchestrator` with no arguments and do all of it from a menu (see\n[Interactive mode](#interactive-mode)).\n\n**Renamed in v0.2.0:** `claude-ctl` is now `claude-orchestrator`, because the tool\nmanages sessions as well as instances. The data directory stays at\n`~/Library/Application Support/claude-ctl/`, so existing instances keep working.\n\nKeep work, school and a second work account signed in side by side:\n\n```\nclaude-orchestrator new work\nclaude-orchestrator new school\nclaude-orchestrator new client --workdir ~/workspace/client   # optional default project\n\nclaude-orchestrator signin work --exclusive     # sign in, one instance at a time\nclaude-orchestrator signin school --exclusive\nclaude-orchestrator signin client --exclusive\n\nclaude-orchestrator project import --from default   # reuse the projects you already have\nclaude-orchestrator project add ~/workspace/api       # or add new ones: every instance gets them\n\nclaude-orchestrator start work && claude-orchestrator start school && claude-orchestrator start client\nclaude-orchestrator agent --install             # menu bar icons to start/stop/switch\n```\n\n`signin --exclusive` closes the other instances while you sign in, because the browser\nhands the login back through a single `claude://` link that only one running Claude can\nreceive. It reopens them when you press Enter.\n\nRun the command with no arguments in a terminal:\n\n```\nclaude-orchestrator\n```\n\nEverything day-to-day can be done from the menu. You don't need to remember commands, session ids or file paths:\n\n```\nClaude Orchestrator\n───────────────────────────────────────\n❯ Work        ● running\n  Personal    ● running\n  School      ○ stopped   not logged in\n───────────────────────────────────────\n  + New profile\n  Projects…\n```\n\n| Screen | Options | \n|---|---|\n| **Home** | Every profile with **running / stopped** , and a warning when it is not logged in. Below them:**+ New profile** and**Projects…** | \n| **Profile** (logged in) | **Start** , or**Stop** /**Restart** when it is running ·**Sessions…** ·**Log in again…** | \n| **Profile** (not logged in) | **Log in** only. The other options appear after you log in | \n| **Sessions…** | The profile's sessions, newest first. Type to fuzzy-filter by title, account or project folder | \n| **A session** | Its details (account, project, start, last activity, messages, size, id) · **Copy to…** ·**Move to…** | \n| **+ New profile** | Asks for a name, creates the profile and opens its menu so you can log in right away | \n| **Projects…** | The registered projects and their state in each profile · **+ Add project** ·**Apply a projects.json…** ·**Export to a projects.json…** ·**Sync all profiles** ·**Import from a profile…** | \n| **A project** | Its folders, permission mode and per-profile state · **Remove…** | \n\n**Keys:** ↑/↓ (or Ctrl-P / Ctrl-N) to move, Enter to choose, Esc to go back (in a\nsearch box, Esc clears the text first), `q` on the home screen to quit, Ctrl-U to clear\na search or text field, Ctrl-C to cancel.\n\n**What happens behind the screens:**\n\n- **Log in** /**Log in again…** run the`signin --exclusive` flow. After you confirm,\nthe other running instances are closed while you log in, so the browser's`claude://` link comes back to the right window. They are reopened when you press\nEnter. Use**Log in again…** when a login has expired or the profile should use a\ndifferent account.\n- **Copy to…** /**Move to…** ask for the target account (skipped when there is only\none other account), then show what will happen and wait for`y` . Instances that\nhave to be closed are shut down one after the other, the session is transferred,\nand they are reopened even if the transfer fails. A copy closes only the target and\nthe source keeps its session. A move closes both and keeps the original as a backup.\n- Anything that writes (logging in, transfers, project changes) asks for confirmation first, then prints the same report as the matching command and waits for Enter.\n- The menu refuses to close an instance it is running inside (for example a terminal in Claude's Code tab), because that would end the menu halfway through. Run it from a regular terminal such as Terminal or iTerm.\n\nA project is a folder that shows up as an environment in the Code tab of **every**\nprofile. Instead of adding them one by one, describe them in a file and apply it:\n\n1. \nCopy the example file and edit the paths: \n\n```\nmkdir -p ~/.config/claude-orchestrator\ncp examples/projects.json ~/.config/claude-orchestrator/projects.json\n```\n\n [`examples/projects.json`](https://github.com/Yuix-Networks/claude-orchestrator/blob/main/examples/projects.json) shows every option: a minimal\nentry, a custom name, extra folders, a per-project permission mode and shared`defaults` . If you already have projects,**Projects… → Export to a projects.json…** writes them to the file so you can start from there.\n2. \nOpen **Projects… → Apply a projects.json…** . The default path is already filled\nin. Press Enter.\n3. \nCheck the preview. The file is checked as a whole first, and every problem (a missing folder, a duplicate name, a misspelled key) is listed at once. Nothing changes until the file is clean. \n\n```\nApply ~/.config/claude-orchestrator/projects.json\n\n  + website  ~/workspace/website\n  + Mobile app  ~/workspace/mobile/app\n  ~ api → API\n  - old-project  (not in the file)\n\n❯ Apply changes\n  Apply and remove the 1 not in the file\n  Cancel\n```\n\n `+` is added,`~` is changed or renamed, and`-` is registered but missing from the\nfile.**Apply changes** keeps those.**Apply and remove…** deletes them, which makes\nthe registry match the file exactly.\n4. \nThe projects are written to every profile. A profile that has not logged in yet gets them on its first start after login. The **Projects…** list shows where each\nproject stands: ✓ in sync, ✗ missing (fix it with**Sync all profiles** ), … waiting\nfor the first login.\n\nThe same file works without the menu (`project apply --dry-run`, `project apply`,\n`project apply --prune`). See [Keeping the list in a file](#keeping-the-list-in-a-file)\nfor the full format and the details of how Claude Desktop stores environments.\n\nSet `CLAUDE_ORCHESTRATOR_HOME` to point any command, the menu included, at a separate\ndata directory. You can then experiment without touching your real profiles:\n\n```\nCLAUDE_ORCHESTRATOR_HOME=/tmp/co-test claude-orchestrator\n```\n\nClaude Desktop stores everything in a single Electron profile, so a second launch either reuses the first one's session or refuses to start. Copying the app bundle works, but the copy falls behind on updates and has to be rebuilt every time.\n\n`claude-orchestrator` uses the mechanism Claude already ships for this:\n\n| What | How | \n|---|---|\n| Separate Electron profile (cookies, sessions, settings) | `--user-data-dir=<per-instance path>` | \n| Separate Claude Code config ( `~/.claude` , sessions) | `CLAUDE_CONFIG_DIR=<per-instance path>` | \n| Separate Dock tile | `open -n` (new application instance) | \n\nThe result: several Claude instances side by side, each working on a different project.\n\n```\ncurl -fsSL https://raw.githubusercontent.com/Yuix-Networks/claude-orchestrator/main/install.sh | sh\n```\n\nThe script downloads the latest release and checks its SHA-256 against the published\nvalue. It installs into `/usr/local/bin` when that is writable and into `~/.local/bin`\notherwise. It never uses `sudo`.\n\n- To pick a version or a directory, set `CLAUDE_ORCHESTRATOR_VERSION=0.2.2` or`CLAUDE_ORCHESTRATOR_DIR=~/bin` in front of`sh` .\n- To uninstall, run `curl -fsSL …/install.sh | sh -s -- --uninstall` . Instance data is\nkept.\n\nRecent Homebrew versions refuse to load formulae from a third-party tap until you trust\nit. Trust the tap **before** installing:\n\n```\nbrew tap yuix-networks/tap\nbrew trust yuix-networks/tap\nbrew install yuix-networks/tap/claude-orchestrator\n```\n\nThe formula lives in [Yuix-Networks/homebrew-tap](https://github.com/Yuix-Networks/homebrew-tap).\nUpgrade later with `brew upgrade claude-orchestrator`.\n\nDownload `claude-orchestrator-<version>.tar.gz` from\n[Releases](https://github.com/Yuix-Networks/claude-orchestrator/releases), extract it and\nput the binary on your `PATH`.\n\n```\nswift build -c release\ncp .build/release/claude-orchestrator /usr/local/bin/\n```\n\n**Requirements:** macOS 13 (Ventura) or later — `open --env` is needed to pass the\nper-instance environment. Claude Desktop must be installed in `/Applications`.\n\n| Command | What it does | \n|---|---|\n| `claude-orchestrator` | Interactive menu: profiles, sessions, projects (see [Interactive mode](#interactive-mode) ) | \n| `claude-orchestrator version` | Print the version | \n| `claude-orchestrator new <name> [--workdir <path>] [--sessions-from <instance>]` | Create an instance; optionally seed a default project and copy another instance's sessions | \n| `claude-orchestrator start <name> [--folder <path>]` | Start the instance (separate profile + config) | \n| `claude-orchestrator stop <name>` | Stop the instance (SIGTERM, then SIGKILL if needed) | \n| `claude-orchestrator list [--json]` | All instances with state, pid and workdir | \n| `claude-orchestrator status <name> [--json]` | Details for one instance | \n| `claude-orchestrator logs <name> [-n N] [-f]` | Instance log lines ( `-f` follows, survives log rotation) | \n| `claude-orchestrator open <name> [--target workdir\\|profile]` | Open a folder in Finder | \n| `claude-orchestrator signin <name> [--restart] [--exclusive]` | Sign-in guide; `--exclusive` closes the other instances while you sign in | \n| `claude-orchestrator doctor [<name>] [--json]` | Health checks: app version, profile, lock, disk, workdir, session | \n| `claude-orchestrator clone <name> --as <new> [--workdir <path>]` | Copy an instance with its profile, config and sessions | \n| `claude-orchestrator project apply [<file>]` | Apply a hand-edited `projects.json` to every instance | \n| `claude-orchestrator memory link\\|unlink\\|status` | Share `~/.claude/CLAUDE.md` with every instance | \n| `claude-orchestrator project …` (alias`env` ) | Projects shared by every instance — Claude Desktop calls them environments (see below) | \n| `claude-orchestrator session …` | List, inspect, copy and move Claude Code sessions (see below) | \n| `claude-orchestrator remove <name> [--purge [--permanent]] [--force] [--yes]` | Delete the instance (alias `delete` ,`rm` ).`--purge` moves its data to the Trash and`--permanent` deletes it for good. A running instance is stopped after you confirm, or right away with`--force` | \n| `claude-orchestrator agent [--install\\|--uninstall]` | Menu bar icons (one per instance), installable as a login item | \n\nEvery `--json` output carries a `schemaVersion` field (currently `1`). It is bumped only\nwhen a field is removed or renamed.\n\nExit codes: `0` success, `1` runtime error, `2` usage error.\n\nClaude Desktop keeps the Code tab's environments per profile and per account. Without\nhelp, you'd create every project again in each instance. `project` keeps one list and\nwrites it into all of them:\n\n```\nclaude-orchestrator project add ~/workspace/api                       # name = folder name\nclaude-orchestrator project add ~/workspace/site --name \"Site\" --folder ~/workspace/shared\nclaude-orchestrator project import --from default    # take over the regular Claude's environments\nclaude-orchestrator project list                     # ✓ synced  ✗ missing  … waiting for sign-in\nclaude-orchestrator project sync                     # re-apply to every instance\nclaude-orchestrator project remove \"Site\"            # also removes it from the instances (--keep: don't)\n```\n\n- The list lives in `config.json` . New instances get it automatically, and every`start` applies it again.\n\nYou can edit the list by hand in a JSON file and apply it, from the menu\n(**Projects… → Apply a projects.json…**, see\n[Setting up projects from a JSON file](#setting-up-projects-from-a-json-file)) or with\n`project apply`. The default file is `~/.config/claude-orchestrator/projects.json`. A\nfully commented example is in [`examples/projects.json`](https://github.com/Yuix-Networks/claude-orchestrator/blob/main/examples/projects.json). The\nsmallest useful file:\n\n```\n{\n  \"projects\": [\n    { \"name\": \"shop\", \"path\": \"~/workspace/shop/shop-backend\",\n      \"folders\": [\"~/workspace/shop/shop-frontend\"] },\n    { \"path\": \"~/workspace/blog\" }\n  ]\n}\nclaude-orchestrator project export ~/.config/claude-orchestrator/projects.json  # start from what you have\nclaude-orchestrator project apply --dry-run    # show the plan, change nothing\nclaude-orchestrator project apply              # add, update and rename projects\nclaude-orchestrator project apply --prune      # also delete projects that are no longer in the file\n```\n\n- \n`name` and`folders` are optional. Paths may use`~` or be relative to the file.\n- \n`defaults` applies to every project.`defaults.folders` is added to each project's\nextra folders (for example a knowledge-base repo every session should see), and`defaults.permissionMode` is used where a project doesn't set its own:\n\n```\n{\n  \"defaults\": { \"folders\": [\"~/workspace/notes\"], \"permissionMode\": \"bypassPermissions\" },\n  \"projects\": [ { \"path\": \"~/workspace/blog\", \"permissionMode\": \"acceptEdits\" } ]\n}\n```\n\n- \n`permissionMode` sets the default permission mode for sessions started in that\nproject's folder. It can be`default` ,`acceptEdits` ,`plan` ,`auto` or`bypassPermissions` . Claude Desktop stores this per account and per folder. For`bypassPermissions` , apply also turns on \"allow bypass mode\" and records the\naccount's opt-in.\n  - An organization policy that disables bypass mode is left alone, and the output names the affected accounts.\n  - Removing `permissionMode` from the file clears it from Desktop. A change that only\ntouches the mode keeps the environment's other settings.\n- \n`//` comments and trailing commas are accepted.\n- \nThe whole file is checked first: every missing folder, duplicate name and misspelled key is reported together, and nothing changes until the file is clean.\n- \nWithout `--prune` , projects missing from the file stay registered. The plan lists\nthem.\n- \nA project whose folder, extra folders or name changed has its environment rewritten in every instance. That resets Desktop settings such as the model for that environment.\n- \nAn environment belongs to an account, so before the first sign-in only the folder is trusted. The environment itself is written on the first `start` after you sign in.\nIn a profile that has seen several accounts, every account gets the project.\n- \nAn environment that already exists for the same folder is left alone, including its name, model and other settings.\n- \n**Claude reads the list file only once per account.** Claude Desktop keeps\nenvironments in its own UI storage (the profile's`Local Storage` ). It imports`ccd-session-presets.json` the first time the account opens in that profile, then\noverwrites the file with its own list. So:\n  - Projects written before the first sign-in show up on their own.\n  - In an instance that has already been opened, stop it and run\n`claude-orchestrator project sync --reset-desktop` (or`project apply --reset-desktop` ). This moves the profile's`Local Storage` to`Local Storage.bak-<time>` and writes the full list, and Claude imports it on the\nnext start.\n  - Sign-in is not affected, since cookies live elsewhere. The instance does lose its UI preferences, such as sidebar layout and dismissed tips.\n  - Instances that are running are refused, because a running Claude would write its list back.\n  - `apply` and`sync` warn when they add environments to an instance that needs this.\n- \n`project remove` keeps a folder trusted if another project or the instance's workdir\nstill uses it.\n\nEach instance has its own Claude Code config directory, so it doesn't read your\n`~/.claude/CLAUDE.md`. `memory` links that file into every instance, so your global\ninstructions are the same everywhere and you edit them in one place:\n\n```\nclaude-orchestrator memory status          # which instances are linked\nclaude-orchestrator memory link            # link every instance\nclaude-orchestrator memory link --force    # also replace an instance's own CLAUDE.md (kept as a backup)\nclaude-orchestrator memory unlink          # remove the links\n```\n\nNew instances, and instances started without a CLAUDE.md, are linked automatically. An\ninstance's own CLAUDE.md is never overwritten without `--force`.\n\nEach instance has its own Claude Code config directory, so its sessions stay with it.\nThe `session` commands move them around:\n\n```\nclaude-orchestrator session list                          # every instance, newest first\nclaude-orchestrator session list --instance work --project ~/workspace/api\nclaude-orchestrator session show 3f2a                     # an id prefix is enough\nclaude-orchestrator session copy 3f2a --to school         # the source keeps its copy\nclaude-orchestrator session move 3f2a --to school         # the source copy goes to a backup\nclaude-orchestrator session last --from work --to school  # move work's latest session\n```\n\n- The name `default` means the Claude install outside claude-orchestrator\n(`~/.claude` ), so`session copy <id> --instance default --to work` brings in\nan existing session.\n- The target must be stopped (or pass `--force` ).`move` also needs the source to be\nstopped.\n- A session id that already exists in the target is refused unless you pass `--force` .\nSo is a project folder that maps to the same directory name as a different path.\n- Files are copied to a temporary name and then renamed into place, so an interrupted\ntransfer never leaves half a transcript. `move` doesn't delete anything. It moves the\nsource files to`~/Library/Application Support/claude-ctl/backups/<time>-<id>/` .\n- The transcript, sub-agent transcripts, TODO files and the project's\n`sessions-index.json` entry are all transferred. The session can be resumed with`CLAUDE_CONFIG_DIR=<target>/claude-code claude --resume <id>` (`session show` prints\nthe exact command).\n- **Claude Desktop's sidebar (experimental).** If the source has a Desktop entry for the\nsession and the target is signed in, a copy is written to the target's Code tab list.\nAccount-specific fields are dropped. The entry appears after the target instance\nrestarts. When the target's account is unknown, you get the`--resume` command\ninstead.\n- Summaries are cached in `~/Library/Application Support/claude-ctl/cache/sessions.json` ,\nkeyed by file size and modification time. The cache is safe to delete.\n\nChats in the Chat tab are not transferred. They are stored in your claude.ai account, not on disk, so an instance signed in to the same account already sees them and an instance signed in to a different account cannot open them.\n\nRun `session move` with no arguments to pick everything on screen:\n\n```\nclaude-orchestrator session move\n```\n\n1. **Pick the session.** Type to fuzzy-filter by title, account or project folder\n(case and Turkish letters are ignored), move with ↑/↓ (or Ctrl-P/Ctrl-N), press Enter.\n2. **Pick the target account.** Every account except the session's owner is listed. This\nscreen is skipped when there is only one other account.\n3. **Confirm.** The Claude instances of both accounts that are running are shut down one\nafter the other, the session is moved, and they are reopened. Press`y` to go ahead;\nEsc goes back a screen.\n\n`session move <id>` opens only the screens it still needs, and `--to` / `--instance`\nnarrow the choice; `--yes` skips the confirmation. Passing both an id and `--to` keeps\nthe old, non-interactive behaviour. Run it from a regular terminal: started from inside\nan instance it would have to shut down (a Code tab terminal), it refuses.\n\n```\nclaude-orchestrator agent --install     # login item\nclaude-orchestrator agent               # run now\n```\n\nEach instance gets a menu bar icon (Claude's icon plus a badge). The menu can start a stopped instance, bring a running one to the front, stop it, open its workdir or profile folder, show its logs and display the sign-in guide. The agent is a single process; it refreshes every 5 seconds.\n\n`claude-orchestrator start` runs exactly this:\n\n```\nopen -n -a /Applications/Claude.app \\\n     --env  CLAUDE_CONFIG_DIR=~/Library/Application\\ Support/claude-ctl/instances/<name>/claude-code \\\n     --args --user-data-dir=~/Library/Application\\ Support/claude-ctl/instances/<name>/profile\n```\n\n`-n` asks macOS for a new application instance, so the second Claude does not join the\nfirst one. The per-instance data path comes from Claude's own Electron profile, and the\nClaude Code CLI is redirected with `CLAUDE_CONFIG_DIR`.\n\n`--env` **must** come before `--args`: everything after `--args` is handed to the app as\nargv, so a misplaced `--env` would reach Claude as a stray argument instead of being\nconsumed by `open`.\n\nBefore starting, the instance profile is seeded so the Code tab opens in the right project:\n\n- `claude_desktop_config.json` →`preferences.localAgentModeTrustedFolders`\n- `ccd-session-presets.json` → a session preset whose`target.cwd` is your workdir\n\n```\n~/Library/Application Support/claude-ctl/\n├── config.json                     # instance definitions\n├── backups/                        # sources of `session move`\n├── cache/sessions.json             # session summary cache\n└── instances/<name>/\n    ├── profile/                    # Electron profile\n    ├── claude-code/                # CLAUDE_CONFIG_DIR\n    └── state.json                  # pid, start time, Claude version\n```\n\n- **No separate Dock icon.** Every instance shows Claude's icon; the menu bar agent's\nbadges are what tell them apart. Giving an instance its own Dock icon requires\nchanging the app's bundle identity, which breaks macOS/Electron security guarantees.\n- **The application menu still says \"Claude\"** (the name is compiled into the app).\n- **Log files are shared.** Claude writes to`~/Library/Logs/Claude/` ;`claude-orchestrator logs` filters by the instance's start time, so the separation is approximate.\n- **The session preset needs an account.** The workdir is seeded as a*trusted folder* immediately, but the \"session preset\" (the entry the Code tab opens with) is written\nonly once the instance knows your Anthropic account — that is, after the first sign-in.\nRun`claude-orchestrator signin <name> --restart` (or restart the instance once) afterwards.\n- **One `claude://` handler.** The OAuth deep link is delivered to a single instance;\nuse`claude-orchestrator signin <name> --exclusive` . It also warns when the regular,\nnon-instance Claude is open, since that one can catch the link too.\n- **Disk usage.** Each instance keeps its own Claude Code CLI download (~500 MB) plus\nits profile.\n- **The Claude Code directory fills up after sign-in.** The per-instance`claude-code/` folder stays empty until the instance is signed in, because the CLI\nonly starts once you use a Claude Code session in that instance.\n- Claude Desktop itself enforces some single-instance behaviour; if an instance refuses\nto start, `claude-orchestrator doctor <name>` explains why.\n\n```\nswift build\nswift test\n./Scripts/smoke.sh     # live end-to-end check (starts real Claude instances)\n```\n\nMIT — see [LICENSE](https://github.com/Yuix-Networks/claude-orchestrator/blob/main/LICENSE).\n\nDeveloped by the **Nameocean Team** — [nameocean.net](https://nameocean.net) ·\n[yuix.org](https://yuix.org). Nameocean is a project of **Yuix Networks Inc**.", "url": "https://wpnews.pro/news/show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos", "canonical_source": "https://github.com/Yuix-Networks/claude-orchestrator", "published_at": "2026-11-10 08:58:22+00:00", "updated_at": "2026-11-10 09:23:13.629114+00:00", "lang": "en", "topics": ["ai-tools", "ai-products", "developer-tools", "ai-agents"], "entities": ["claude-orchestrator", "Claude Desktop", "Claude Code", "macOS", "claude-ctl", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos", "markdown": "https://wpnews.pro/news/show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos.md", "text": "https://wpnews.pro/news/show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos.txt", "jsonld": "https://wpnews.pro/news/show-hn-run-multiple-claude-desktop-instances-side-by-side-on-macos.jsonld"}}