Skip to main content
Jason Finance
AI Startup

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. 1.Multiple Claude Code Remote Controls: first decide whether to split Linux users
  2. 2.Same repo: open a content-only working directory with git worktree
  3. 3.One systemd service per environment
  4. 4.Names: –name, service names and conversation titles are separate layers
  5. 5.–spawn=worktree and –capacity: things to watch
  6. 6.How the three environments divide the work
  7. 7.When one of them won’t start (status=1/FAILURE)
  8. 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):

  • -b creates a new branch from the given starting point and checks it out in the new worktree. If the branch already exists, -b refuses to create it. In that case use git 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 HEAD and 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
  • MemoryHigh and MemoryMax are 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:

  1. Looking at the wrong working directory: a new session is in its own worktree, not necessarily the content/editorial one you created. Check before you start:

    pwd && git status -sb && git worktree list
  2. 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.

  3. 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

About the author

Photo of Jason

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.

  1. Loading comments