Skip to content

How to Set Up Claude Code Remote-Control as a Launchd Service on Mac

I wanted to access Claude Code from my phone. The remote-control feature seemed perfect, but I didn’t want to manually start it every time my Mac rebooted or if it crashed. I needed a permanent background service that would start automatically and stay running.

The Problem with Manual Startup

I initially tried running claude remote-control manually each time I needed it. This quickly became tedious:

  1. Open Terminal
  2. Navigate to my project directory
  3. Run claude remote-control
  4. Keep the terminal window open
  5. Repeat whenever my Mac restarted or the process crashed

I tried using a Telegram bot as a bridge initially, but that added unnecessary complexity and dependencies. I wanted something native to macOS that would just work.

Finding the Right Approach

After some research, I discovered that claude remote-control should be run as a standalone command, not inside a Claude Code session. This was my first mistake - I had been trying to run it from within Claude Code itself.

The key insight was that macOS has a built-in service manager called launchd that can manage long-running processes. This is exactly what I needed.

Step 1: Enable Remote Control in Claude Code

First, I needed to enable the remote-control feature in Claude Code:

Enable remote control
claude /config

This opens the configuration menu where I enabled the remote-control option. I made note of the authentication token it provided - I’d need this later to connect from my phone.

Step 2: Find the Claude Binary Path

I needed the full path to the claude binary for the launchd plist file:

Find Claude binary path
which claude

Output:

Output
/opt/homebrew/bin/claude

Your path might be different depending on how you installed Claude Code. If you used npm or yarn, it might be in a different location. I’m using Homebrew on an Apple Silicon Mac, so mine is in /opt/homebrew/bin/.

Step 3: Create the LaunchAgent Plist File

I created a new plist file in the LaunchAgents directory:

Create plist file
touch ~/Library/LaunchAgents/com.claude.remote-control.plist

LaunchAgents run as the current user and start when you log in, which is exactly what I needed. LaunchDaemons run as root and start at boot, but that would be overkill for this use case.

Then I opened it in my editor and added the configuration:

com.claude.remote-control.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.claude.remote-control</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/claude</string>
<string>remote-control</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/cowrie/projects</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/claude-remote-control.log</string>
<key>StandardErrorPath</key>
<string>/tmp/claude-remote-control.error.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>

Let me explain each key:

  • Label: A unique identifier for the service. I used a reverse-domain naming convention.
  • ProgramArguments: The command to run. The first string is the full path to the binary, followed by arguments.
  • WorkingDirectory: This is crucial - it sets the directory where Claude Code will operate. I set it to my main projects folder.
  • RunAtLoad: Start the service immediately when loaded.
  • KeepAlive: Automatically restart the service if it crashes. This was the key feature I needed.
  • StandardOutPath/StandardErrorPath: Log files for debugging. I put them in /tmp so they’re cleared on reboot but accessible for troubleshooting.
  • EnvironmentVariables: I included the PATH to ensure Claude can find its dependencies.

Step 4: Load and Start the Service

I loaded the service using launchctl:

Load the launchd service
launchctl load ~/Library/LaunchAgents/com.claude.remote-control.plist

This should start the service immediately because of the RunAtLoad key. But I wanted to verify it was actually running.

Step 5: Verify the Service is Running

I checked the service status:

Check service status
launchctl list | grep claude

Output:

Output
12345 0 com.claude.remote-control

The first number is the process ID (PID), and the second is the exit code. An exit code of 0 means the service is running successfully.

I also checked the log files:

Check service logs
tail -f /tmp/claude-remote-control.log

This showed me the remote-control startup output, including the URL I could use to connect from my phone.

Troubleshooting Common Issues

When I first set this up, I made several mistakes that prevented the service from starting correctly.

Incorrect Binary Path

My first attempt failed because I used the wrong path to the Claude binary:

Check launchd error logs
tail -20 /tmp/claude-remote-control.error.log

The error log showed:

Error output
launchd: Could not find program: /usr/local/bin/claude

I had assumed the binary was in /usr/local/bin but it was actually in /opt/homebrew/bin. Running which claude again and updating the plist fixed this.

Missing Working Directory

I initially forgot to set the WorkingDirectory. The service started but Claude Code couldn’t find my projects. Adding the WorkingDirectory key with my projects folder path resolved this.

Service Not Auto-Restarting

At one point, the service kept crashing immediately. I checked the error log:

View error logs
cat /tmp/claude-remote-control.error.log

The issue was a missing configuration file. Once I fixed that, the KeepAlive setting worked correctly and the service restarted automatically.

Reloading After Plist Changes

When I needed to modify the plist file, I had to unload and reload the service:

Reload launchd service
launchctl unload ~/Library/LaunchAgents/com.claude.remote-control.plist
launchctl load ~/Library/LaunchAgents/com.claude.remote-control.plist

Simply editing the file doesn’t apply changes - you must reload the service.

Why This Approach Works Better

This launchd approach gave me several advantages over manual startup:

Automatic Startup: The service starts when I log in, so it’s always ready. I don’t need to remember to start it manually.

Auto-Restart: If the remote-control process crashes, launchd automatically restarts it. This keeps the service available even when unexpected errors occur.

No External Dependencies: I don’t need Telegram bots or third-party services. Everything runs locally on my Mac using built-in macOS tools.

Persistent Access: I can now reliably connect to Claude Code from my phone anytime my Mac is on. The service stays running in the background, ready for connections.

Easy Management: Using standard launchctl commands, I can start, stop, reload, and check status of the service. It’s well-documented and familiar to anyone who’s worked with macOS services.

Managing the Service

Here are the commands I use to manage the service day-to-day:

Stop the service
launchctl stop com.claude.remote-control
Start the service
launchctl start com.claude.remote-control
Unload (disable) the service
launchctl unload ~/Library/LaunchAgents/com.claude.remote-control.plist
Reload the service after plist changes
launchctl unload ~/Library/LaunchAgents/com.claude.remote-control.plist && \
launchctl load ~/Library/LaunchAgents/com.claude.remote-control.plist

Connecting from My Phone

With the service running, I can now connect from my phone. The remote-control feature provides a URL that looks like:

Remote control URL
https://claude.ai/remote/xxxxx-xxxxx-xxxxx

I bookmarked this URL on my phone’s home screen. When I open it, I’m connected to my Mac’s Claude Code session and can interact with it just like I’m at my desk.

Summary

In this post, I showed how to set up Claude Code’s remote-control as a launchd service on Mac. The key point is to run claude remote-control as a standalone command, use the correct binary path, set a valid working directory, and configure auto-restart with KeepAlive. This gives you permanent, reliable access to Claude Code from any device without manual intervention.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments