Running Claude Code on Your Mac from Your iPhone

· 9 min read

I was sitting in a coffee shop, phone in hand, when I realized the Claude Code session on my Mac at home had the exact context I needed. The codebase was loaded, the conversation history had all the decisions, and I couldn’t get to it. So I spent an afternoon figuring out how to control Claude Code on my Mac from my iPhone and keep sessions alive across app switches. Here’s everything I learned, including the problems nobody warns you about.

What you need

The two ways to control Claude Code remotely

Before diving into the setup, it’s worth understanding that there are two paths to running Claude Code from your phone.

Remote Control is Claude Code’s built-in feature. You run /remote in a terminal session on your Mac, scan a QR code with the Claude iOS app, and you can send messages to that session from your phone. It works over the internet with no networking setup required. Sounds perfect, right?

In practice, Remote Control has rough edges. User input prompts (like permission approvals or yes/no confirmations) don’t always transmit properly to the mobile app. Sometimes the session goes silent and you can’t tell whether it’s waiting for input or still processing. When that happens, you have no way to intervene from the phone. I’ve lost sessions this way more than once.

SSH/Mosh via Tailscale is the alternative that ended up becoming my primary method. You get a full terminal on your phone with complete visibility into what Claude Code is doing. If something stalls, you can see why, approve a prompt, or interrupt and recover. It requires more setup (which is what this post covers), but once it’s working, it’s reliable.

What I’ve settled on is a combination of both. I start Claude Code on the Mac, run /remote to pair it with my phone, and use the Claude iOS app for quick interactions. When a session becomes unresponsive (a permission prompt that didn’t render, a confirmation the app missed), I switch to Termius and connect via Mosh to see the full terminal and take action. Two tools, one session, each covering the other’s gaps.

Get Tailscale working first

You need your Mac and iPhone on the same network, even when they’re on different Wi-Fi networks or one is on cellular. That’s what Tailscale does.

Tailscale builds a peer-to-peer VPN mesh using WireGuard encryption under the hood. Instead of configuring port forwarding or firewall rules on your router, each device gets a stable IP on a private network (the 100.x.x.x range) and can reach every other device directly. A coordination server handles key exchange and NAT traversal, but the actual traffic flows device-to-device, not through Tailscale’s servers. Free for personal use, zero configuration on the network side.

Install it on both your Mac and iPhone, sign in with the same account. On the Mac, run:

Terminal window
tailscale status

You’ll see something like:

100.89.142.65 your-macbook you@ macOS -
100.84.80.114 your-iphone you@ iOS idle

Note your Mac’s Tailscale IP (the 100.x.x.x address). Use this IP in Termius, not the MagicDNS hostname. MagicDNS resolution can be unreliable from mobile. The raw IP is more dependable.

Even if you plan to primarily use Remote Control, you still need Tailscale. Remote Control handles the Claude Code communication channel, but you need a network path to your Mac for SSH/Mosh access when Remote Control can’t relay input properly.

The real problem: iOS kills your SSH connection

Termius is an SSH and Mosh client for iOS. It connects to remote machines and renders a full terminal emulator on your screen, with support for both TCP-based SSH and UDP-based Mosh connections.

You’ll get SSH working from Termius pretty quickly. The real problem comes next. Every time you switch away from Termius on your iPhone, even briefly to check a message, the SSH connection dies. Your Claude Code session is killed.

This is an iOS limitation, not a Termius bug. iOS aggressively suspends background apps and kills their network connections. There’s no workaround on the iPhone side. Enabling location tracking, background app refresh, or any other iOS setting won’t help. Only VoIP, audio playback, and navigation apps get persistent background network access on iOS.

I tried everything. Location tracking permissions, background app refresh settings, keep-alive configurations in Termius. None of it worked. The connection dropped every single time I switched apps.

Mosh: the actual fix

Mosh (Mobile Shell) is designed for exactly this problem. Where SSH relies on a persistent TCP connection that breaks the moment iOS suspends the app, Mosh uses UDP and maintains session state on the server side. When Termius comes back to the foreground, Mosh resynchronizes instantly. Your session survives app switches, network changes, and even brief connectivity drops.

On your Mac:

Terminal window
brew install mosh

Then change the connection type in Termius from SSH to Mosh. That’s it in theory. In practice, you’ll hit a wall.

The PATH problem that wastes an hour

After installing Mosh, you’ll try connecting from Termius and see this:

command not found: mosh-server

Homebrew installs to /opt/homebrew/bin/ on Apple Silicon Macs. When Termius SSHes in and runs the mosh command, it uses a non-login, non-interactive shell that doesn’t load your .zshrc or .zprofile. So /opt/homebrew/bin/ isn’t in the PATH, and mosh-server can’t be found.

Fix it in two steps

First, create symlinks to /usr/local/bin/:

Terminal window
sudo ln -s /opt/homebrew/bin/mosh-server /usr/local/bin/mosh-server
sudo ln -s /opt/homebrew/bin/mosh-client /usr/local/bin/mosh-client

If that alone doesn’t work (it didn’t for me), Termius’s shell context might not even include /usr/local/bin. The fix is to create ~/.zshenv:

Terminal window
echo 'export PATH="/usr/local/bin:$PATH"' > ~/.zshenv

Why .zshenv specifically? It’s the only zsh config file that runs in ALL shell modes: interactive, non-interactive, login, non-login. That’s why .zshrc and .zprofile don’t work here. Termius executes commands in a non-interactive, non-login context, and .zshenv is the only file that catches it.

This has no security implications. /usr/local/bin is already a standard macOS system path listed in /etc/paths. You’re just making sure it’s available in contexts where path_helper doesn’t run.

Connect from Termius

  1. Open your Mac host connection in Termius
  2. Edit the connection settings
  3. Change the connection type from SSH to Mosh
  4. Host: your Mac’s Tailscale IP (e.g., 100.89.142.65)
  5. Connect

Now switch away from Termius, check your messages, come back. The session is still alive. This is the moment it clicks.

Claude Code authentication: the keychain problem

When you first try running claude over Mosh, it might say it’s not authenticated. Even though you already authenticated on the Mac directly. This confused me for a while.

The reason: Claude Code stores its OAuth tokens in the macOS login keychain. SSH and Mosh sessions don’t automatically unlock the keychain. So Claude Code can’t access its stored credentials.

The fix is to add this block to your ~/.zshrc:

Terminal window
# Unlock login keychain for SSH sessions (allows Claude Code to access stored credentials)
if [[ -n "$SSH_CONNECTION" ]]; then
security unlock-keychain ~/Library/Keychains/login.keychain-db 2>/dev/null
fi

This detects SSH/Mosh sessions via $SSH_CONNECTION and runs security unlock-keychain, which prompts for your Mac password once when you first connect. After entering it, the keychain stays unlocked for that session and Claude Code can access its stored tokens.

The password prompt doubles as a security gate. Only someone who knows your Mac password can get a working Claude Code session over SSH.

If you’ve never authenticated Claude Code on this Mac at all, you’ll need to run claude auth login first from a local terminal (not SSH) and complete the browser OAuth flow. After that, the .zshrc block handles every remote session automatically.

The combo workflow: Remote Control + Mosh as a pair

Here’s how I actually use this day to day:

  1. Start Claude Code on my Mac (either at my desk or via Mosh from my phone)
  2. Run /remote to get the pairing QR code
  3. Scan it with the Claude iOS app
  4. Use the Claude app for quick interactions: “run the tests”, “what’s the status of the PR”, “fix the linting error”
  5. When Remote Control becomes unresponsive (a permission prompt that doesn’t render, a session that goes quiet), switch to Termius
  6. In the Mosh terminal, I can see the full terminal state, approve prompts, or Ctrl+C to recover

Remote Control is faster for simple back-and-forth because you don’t need a terminal keyboard. Mosh gives you full visibility and control for when things need intervention. They complement each other well. I wouldn’t want to rely on either one alone.

Troubleshooting

“command not found: mosh-server”: The symlinks or .zshenv fix didn’t take. Verify with ls -la /usr/local/bin/mosh-server and cat ~/.zshenv.

Connection drops immediately: Check that Tailscale is active on both devices. Open the Tailscale app on your iPhone and make sure the toggle is on.

Claude Code says “not authenticated” over SSH: The macOS keychain is locked. Add the security unlock-keychain block to your ~/.zshrc as described above.

Claude Code session lost after switching apps: You’re probably still using SSH instead of Mosh. Edit the Termius connection and change the type to Mosh.

Mosh connects but freezes: Check if your Mac’s firewall is blocking UDP ports 60000-61000. Mosh uses UDP on these ports. Go to System Settings, Network, Firewall, and either disable it or add an exception.

Remote Control session goes silent: This typically means Claude Code is waiting for input that the mobile app didn’t render. Switch to Termius, Mosh into your Mac, and check the terminal. You’ll usually find a prompt waiting for a yes/no confirmation or a permission approval.

What this unlocks

Once this setup is working, you have a persistent Claude Code session accessible from your phone anywhere in the world. You can start a coding task at your desk, walk away, and pick it up from the couch or a coffee shop. The session survives network changes, app switches, and even brief connectivity drops.

In the next post, I’ll cover a different approach: using Claude’s Dispatch mode to send tasks to your Mac without a terminal at all. Different tradeoffs, different use cases.

Subscribe to the Newsletter

Get notified when new posts are published. No spam, unsubscribe anytime.

You'll be taken to Substack to complete your subscription.