Running Multiple Claude Code Remote Controls on One Server: Linux Users, systemd and git worktree
Multiple Claude Code Remote Controls on one server: Linux users by trust, one systemd service each, git worktree for dev vs writing, --spawn=worktree limits.
On this page
- 1.Multiple Claude Code Remote Controls: first decide whether to split Linux users
- 2.Same repo: open a content-only working directory with git worktree
- 3.One systemd service per environment
- 4.Names: âname, service names and conversation titles are separate layers
- 5.âspawn=worktree and âcapacity: things to watch
- 6.How the three environments divide the work
- 7.When one of them wonât start (status=1/FAILURE)
- FAQ
In October 2026 I put the Claude Code Remote Controls for several projects on an Oracle Ubuntu ARM server, kept running by systemd, and the Claude App on my phone showed three environments: development, content and a back-end service. This post covers how to split users, services and working directories when running multiple Claude Code Remote Controls on one server.
The short version: one systemd user service per environment; projects with different levels of trust go to different Linux users; when the same repo needs both feature development and writing, use git worktree to open a second working directory instead of a second repo. --spawn=worktree separates working directories, not permissions. Real isolation comes from separate Linux users, separate credentials and least-privilege GitHub access.
This is my deployment log and write-up from October 2026, with project and user names replaced by examples such as
project-a. For Claude Code Remote Control flags, go by the official documentation (checked 12 October 2026): it requires a Pro, Max, Team or Enterprise plan and does not support API keys; on Team and Enterprise an Owner has to turn it on in the admin settings. The approach here does not guarantee that services stay online forever.
Multiple Claude Code Remote Controls: first decide whether to split Linux users
| Approach | Pros | Cons | Good for |
|---|---|---|---|
| One Linux user per project | Each has its own ~/.claude/, SSH key, GitHub authorization and systemd services, so permissions are easy to separate |
Each user has to sign in to Claude and set up GitHub separately | High-risk projects such as production or databases |
| Same user, different repos or worktrees | Shares one Claude sign-in and GitHub CLI authorization, set up once | No operating-system-level isolation; one session can read other projectsâ files | Several low-risk projects owned by one person, different work in the same repo |
systemd settings such as NoNewPrivileges=true are not a complete sandbox either. Remote Controls that share the same Linux user can still read and write any files the others can access.
Create the project user and enable linger (so services donât depend on a login):
sudo adduser --disabled-password --gecos '' project-a
sudo loginctl enable-linger project-a
loginctl show-user project-a -p Linger
Each new user has to install and sign in to Claude Code separately, and set up its own GitHub SSH key and API authorization. For the API authorization needed to read Issues, see Git Can Push, but Claude Code Canât Read GitHub Issues. Donât copy another userâs ~/.claude/ sign-in data over directly.
Same repo: open a content-only working directory with git worktree
In my case it was one website repo, and I wanted one Remote Control focused on code and another focused on writing posts and SEO. The approach is to use git worktree to open a second working directory from the same repo, on its own branch.
# As the Linux user that owns the repo
cd ~/projects/project-a
git status -sb
git fetch origin
# First check whether the branch already exists
git branch --list 'content/editorial'
git branch -r --list 'origin/content/editorial'
# Run only if content/editorial doesn't exist yet
git worktree add -b content/editorial ~/projects/project-a-content origin/main
git worktree list
A few rules (official git worktree documentation):
-bcreates a new branch from the given starting point and checks it out in the new worktree. If the branch already exists,-brefuses to create it. In that case usegit worktree add <path> <existing-branch>instead of forcing-b.- The same branch canât be checked out in two worktrees at once; Git refuses by default.
- The new worktree shares the object store and refs with the original repo; only files such as
HEADand the index are separate.
Why not just copy it with cp -r? A copy includes .git, giving you two independent repos, which makes syncing and branch relationships harder to manage later. Worktrees share the same Git database, so itâs always clear which branch youâre on.
One systemd service per environment
Each Remote Control gets its own systemd user service, with its own working directory and display name. Using the content worktree as an example, create the unit file as the project-a user:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/claude-project-a-content.service <<'SERVICE_UNIT'
[Unit]
Description=Claude Code Remote Control - Project A Content
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/home/project-a/projects/project-a-content
ExecStart=/home/project-a/.local/bin/claude remote-control --name "VM1 - Project A - Content" --spawn=worktree
Restart=on-failure
RestartSec=15
Environment=HOME=/home/project-a
Environment=PATH=/home/project-a/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MemoryHigh=2500M
MemoryMax=3500M
NoNewPrivileges=true
UMask=0077
[Install]
WantedBy=default.target
SERVICE_UNIT
tail -n 4 ~/.config/systemd/user/claude-project-a-content.service
MemoryHighandMemoryMaxare limits for each service, not totals for the whole server. When several services run at once, adjust them to the serverâs actual memory.- After writing the file, check that it ends with
[Install]. Iâve had a heredoc not close properly and leave the file missing a section.
Before the first start, switch to this user, go into the working directory and run ~/.local/bin/claude remote-control --name "..." --spawn=worktree once by hand. The first time it asks Enable Remote Control? (y/n). Once you see Connected and confirm the environment appears on your phone or the web, press Ctrl+C to stop it and hand it over to systemd.
Then go back to the admin account to enable the service. Running systemctl --user directly in a sudo -iu shell can give No medium found, so specify the user bus from the admin account:
PROJECT_USER=project-a
uid=$(id -u "$PROJECT_USER")
sudo -u "$PROJECT_USER" \
XDG_RUNTIME_DIR="/run/user/$uid" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
systemctl --user daemon-reload
sudo -u "$PROJECT_USER" \
XDG_RUNTIME_DIR="/run/user/$uid" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
systemctl --user enable --now claude-project-a-content.service
For the cause and a full explanation, see What to Do When systemctl âuser Says No medium found. When adding or restarting services, touch only the one youâre working on. Donât restart them all at once, or you may interrupt work running in other environments.
The official documentation also notes that in server mode the claude remote-control process exits after a network outage of about 10 minutes and has to be started again. Whether Restart=on-failure brings it back depends on how the process exits, so check the journal. Donât assume systemd can handle every disconnect or expired sign-in.
Names: âname, service names and conversation titles are separate layers
| Layer | Where itâs set | Purpose |
|---|---|---|
| Remote Control environment name | --name flag |
Shown in the environment list on claude.ai/code and in the Claude App |
| systemd service name | The unit fileâs filename | Managing the service and reading logs on the server |
| Conversation title | Each session in the Claude App or on the web | Telling individual pieces of work apart |
| Git branch | git worktree add, git switch |
Deciding where changes land |
I suggest sticking to one naming scheme, such as âserver - product - roleâ: VM1 - Project A - Development, VM1 - Project A - Content, with matching service names claude-project-a.service and claude-project-a-content.service. Whichever environment you see on your phone, you know which service on the server to check logs for.
At one point I saw three environments on my phone and worried that someone had changed the names. Renaming the display name in the App generally doesnât change the Git repo, branches or unit files. But how the name appears after the service restarts needs to be checked against the current Claude Code version; I canât promise it will keep the new name or revert to the old one.
âspawn=worktree and âcapacity: things to watch
According to the official documentation, --spawn has several modes: same-dir, worktree and session. In worktree mode, each on-demand session gets its own git worktree, so the working directory must be a git repo. --capacity is the limit on concurrent sessions, with a default of 32. During my deployment the service log once showed Capacity: 1/32 · New sessions will be created in an isolated worktree; that was the output at the time and doesnât mean every plan or version is the same.
Three common misunderstandings:
-
Looking at the wrong working directory: a new session is in its own worktree, not necessarily the
content/editorialone you created. Check before you start:pwd && git status -sb && git worktree list -
Assuming itâs already live: a commit on a branch is not the same as a merge or a deployment. Check the PR, CI and deployment status.
-
Assuming different sessions wonât conflict: two Claude sessions can still change the same code or the same post. Decide up front who owns which files, and merge through PR review.
--spawn=worktree is not operating-system-level isolation. Sessions under the same Linux user all have the same permissions.
How the three environments divide the work
My deployment ended up split into three kinds of work, with the names changed to generic ones:
| Environment | What it does | Working directory | GitHub flow |
|---|---|---|---|
| Development | Site features, front end, bug fixes | Website repo | Feature branch â PR |
| Content | Posts, SEO, content updates | A separate worktree of the same repo | Content branch or feature branch â PR |
| Back-end service | The back end of another, separate project | A separate repo | Stricter review and deployment permissions |
The content environment shouldnât change database schemas, account permissions, environment variables or production deployment settings âwhile itâs at itâ. Rules written only in CLAUDE.md are not technical access control. When you need hard isolation, use separate Linux users, separate credentials and least-privilege GitHub access. A shared repoâs CLAUDE.md affects every worktree; writing guidelines meant only for the content environment can go in a separate file that you reference explicitly when starting work.
Seeing three names on your phone doesnât mean all three services are running. In one of my terminal logs from 11 October 2026, the development environment was active (running) while the back-end service was inactive (dead) at the time. So after every change, check each of the three services separately.
When one of them wonât start (status=1/FAILURE)
Iâve had one Remote Control connect normally while another, under the same Linux user, exited soon after starting, with systemd showing code=exited, status=1/FAILURE. The logs I had werenât enough to confirm the root cause, and they donât show that âone user canât run two Remote Controlsâ either. Troubleshooting order:
# From the admin account: 1. Stop the failing one first so repeated retries don't muddy the picture
PROJECT_USER=project-a
SERVICE=claude-project-a-content.service
uid=$(id -u "$PROJECT_USER")
sudo -u "$PROJECT_USER" \
XDG_RUNTIME_DIR="/run/user/$uid" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
systemctl --user stop "$SERVICE"
# 2. Read the recent logs, not just the last few lines of status
sudo -u "$PROJECT_USER" \
XDG_RUNTIME_DIR="/run/user/$uid" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
journalctl --user -u "$SERVICE" -n 100 --no-pager
# 3. Run it by hand as the same user in the same working directory to see the actual error
sudo -iu "$PROJECT_USER"
cd ~/projects/project-a-content
~/.local/bin/claude remote-control --name "VM1 - Project A - Content" --spawn=worktree
Checks you can verify one by one from the logs: the Claude sign-in status, the CLI version, whether the working directory exists and is a git repo, the userâs HOME and PATH, whether Enable Remote Control? is still unanswered, the network, memory, and whether a process with the same name is already running. Among the error messages in the official documentation, You must be logged in to use Remote Control appears when youâre not signed in or arenât using a claude.ai subscription, and Remote Control requires claude.ai subscription auth. appears when the ANTHROPIC_API_KEY environment variable is set. If running it by hand also fails, keep the exact error text rather than guessing whether itâs OAuth, the network or the concurrent session limit.
For the full steps for the server, the Claude Code installation and the first always-on service, see Running Claude Code Remote Control on a Free Oracle Cloud Server. For the role these tools play across the whole site, see Jason Finance from Zero to One.
FAQ
Can one server run multiple Claude Code Remote Controls?
Yes. Give each environment its own systemd user service, each with its own --name for the name shown in the Claude App list and its own working directory. My October 2026 deployment ran several Remote Control services on the same Ubuntu ARM server, but that doesn't guarantee every service stays online forever or needs no maintenance.
Should multiple Remote Controls use different Linux users or the same one?
It depends on the trust boundary. Separate users each have their own Claude sign-in, SSH key, GitHub authorization and systemd services, which gives better isolation, but each one has to authenticate again. A single user is more convenient, but every project shares the same operating system permissions. For high-risk projects such as production or databases, use separate users and grant least privilege.
How do I separate development and writing in the same GitHub repo?
You don't need a second repo. Use git worktree to create a second working directory from the same repo on its own branch, such as content/editorial, then give it its own Remote Control service. Check first that the branch doesn't exist; if it does, don't use -b.
What is --spawn=worktree, and does it count as isolation?
--spawn=worktree gives each on-demand Remote Control session its own git worktree, and it must run inside a git repo. It isolates the working directory, not operating system permissions, and it doesn't guarantee the session is on the branch you expect. Check the branch and the diff before opening a PR.
What is the default for --capacity?
According to the official documentation, --capacity is the limit on concurrent sessions, and the default is 32. My deployment logs once showed Capacity: 1/32; that was the output at the time and doesn't mean every plan or version is the same.
If I rename a Remote Control on my phone, does it affect the repo or the service?
Conversation titles, the environment display name, the --name flag and the systemd service name are separate layers. Changing the display name in the App generally doesn't change the Git repo, branches or unit files, but how the name appears after the service restarts needs to be checked against the current Claude Code version.
Related articles
- Building a Website Without Reading Code: How I Built Jason Finance Cheaply with Claude Code + GitHub + Cloudflare, and What It CostsI can't read code. Jason Finance runs on Claude Code, GitHub, Cloudflare Workers and Supabase free plans. The setup, costs, AI permissions and limits.
- Git Can Push, but Claude Code Can't Read GitHub Issues: SSH vs GitHub API PermissionsClaude Code can't read GitHub Issues but git push works? SSH keys only cover Git. Sign in with gh CLI, check scope, protect the token, go from Issue to PR.
- Running WordPress Without Knowing SSH: How Jason Career Manages Its Site with Claude Code, SSH and WP-CLIJason Career is a WordPress site with 65 plugins, a shop and courses. I can't read SSH or code, so Claude Code runs it: 10 days of work, problems, safeguards.

Jason
Account Manager in Google Large Customer Sales and Columbia MBA admit, sharing the money tools and experience he actually uses.
Comments
Sign in with Google to comment. Your comment will not show your Google profile picture or name; it appears under a randomly generated nickname to protect your privacy.
By signing in you agree to this site's privacy policy.