Run Claude Code or Codex over SSH from your phone

Put the coding agent on a server, start it in a session that survives disconnects, and check on it from your phone whenever you like. Here is a setup that is quick to build and safe to leave running.

updated

Quick answer. On a Linux server, create a user, log in with an SSH key, install Node.js and the agent CLI, and start the agent inside tmux. Detach, lock your phone, and reattach later from any SSH client to see how far it got.

npm install -g @anthropic-ai/claude-code    # or: npm install -g @openai/codex
tmux new -A -s agent
cd ~/projects/app && claude                 # or: codex

Why run the agent on a server

Coding agents such as Anthropic's Claude Code and OpenAI's Codex CLI work in a terminal: they read your repository, edit files, run commands and tests, and ask before risky steps. A long task can take many minutes. On a laptop that means keeping the lid open and the network up. On a server, the agent keeps working while you are on a train, and your phone becomes the window you check through.

The setup has three parts: a server you can reach over SSH with a key, the agent running in a session that does not end when you disconnect, and an SSH client on your phone that can draw the agent's full-screen interface.

Step 1: a server and a dedicated user

Any Linux machine works: a small VPS, a cloud VM, or a computer at home. The agent itself is light because the model runs at the provider; size the machine for your project's builds and tests. Create a separate user for the agent's work, without sudo, so its commands cannot touch the rest of the system:

sudo adduser dev
sudo mkdir -p /home/dev/.ssh
sudo cp ~/.ssh/authorized_keys /home/dev/.ssh/
sudo chown -R dev:dev /home/dev/.ssh
sudo chmod 700 /home/dev/.ssh && sudo chmod 600 /home/dev/.ssh/authorized_keys

Copying your own authorized_keys is a shortcut; you can also add only the key of the device you will use, as in Add an SSH key to a server.

Step 2: key login, passwords off

Use an SSH key for every device you connect from, ideally a separate key for your phone with a passphrase. Create one with ssh-keygen -t ed25519 or the generator on this site, and confirm that key login works for the new user before you change anything else:

ssh [email protected]

Then turn off password logins and direct root logins, so bots guessing passwords get nowhere:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
EOF
sudo sshd -t && sudo systemctl reload ssh    # sshd on Fedora and RHEL

Keep your current session open and test a new login from a second terminal before closing it. Some cloud images set PasswordAuthentication yes in a file of their own inside sshd_config.d; check with sudo sshd -T | grep -i passwordauthentication.

Step 3: install the agent CLI

Both CLIs below are distributed through npm and need a current Node.js. Install Node.js from your distribution, from NodeSource or with nvm; nvm installs into your home folder, so global packages do not need sudo. Log in as the dev user and run:

# Claude Code
npm install -g @anthropic-ai/claude-code
claude

# Codex CLI
npm install -g @openai/codex
codex

The first start asks you to sign in or to provide an API key. On a server without a browser, the CLI shows a link or code to complete in a browser on another device; follow its prompts. The vendors also publish other installers and change details between releases, so check their current documentation if a step looks different.

Clone the project the agent will work on. Give the server its own deploy key or SSH key for the git host instead of copying your laptop's key there, as described in deploy keys.

ssh-keygen -t ed25519 -C "dev@agent-server" -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub     # add as a deploy key or to a machine account
git clone [email protected]:owner/app.git ~/projects/app

Step 4: keep it running when you disconnect

A program started in a plain SSH session is tied to it. When the connection drops, because you locked the phone or changed networks, the shell is hung up and the agent usually exits mid-task. There are three ways around it.

tmux: the interactive session lives on the server

tmux new -A -s agent          # attach, or create if missing
cd ~/projects/app && claude    # or codex
# detach with Ctrl+b, then d; the agent keeps working
tmux attach -t agent           # later, from any device

This is the most flexible option and works with any client. Make it automatic with ssh -t [email protected] tmux new -A -s agent. More on tmux, screen and keepalives in SSH connection keeps dropping.

nohup: a non-interactive run with a log

Both CLIs have a non-interactive mode that takes a prompt and exits when done: claude -p and codex exec. Combined with nohup it survives the disconnect and writes everything to a file:

cd ~/projects/app
nohup claude -p "Run the test suite and fix the failing tests" > ~/agent.log 2>&1 &
tail -f ~/agent.log

In this mode the agent cannot ask you questions, so what it may do without approval depends on its settings. Read the CLI's documentation on permissions before you leave it alone.

A client that keeps the session

The third way needs no setup on your side. Termphin, an SSH client from the team behind this site, uploads a small open-source helper to the server that keeps the shell running when the connection drops and reattaches you with the screen as it was. You start the agent as usual, lock the phone, and come back to the same view.

Step 5: private access with a mesh VPN

An SSH port on the internet with key-only login is reasonable. Closing it is better. A mesh VPN such as Tailscale, or your own WireGuard, gives the server and your phone private addresses that only your devices can reach:

  1. Install the VPN on the server and on your phone, and sign both into the same network.
  2. Connect to the server's private address or name instead of its public IP, and check that it works.
  3. Only then restrict SSH to the VPN interface in the firewall.
sudo ufw allow in on tailscale0 to any port 22 proto tcp
sudo ufw delete allow OpenSSH
sudo ufw enable
sudo ufw status verbose

Keep the cloud provider's web console at hand in case you lock yourself out. Your SSH client sees nothing special: just a different host name.

Checking on a long run from your phone

Connect, reattach and look around:

tmux attach -t agent       # the agent's screen, as you left it
git -C ~/projects/app status
git -C ~/projects/app diff --stat
tail -n 50 ~/agent.log     # for nohup runs

A few things make this pleasant on a small screen:

  • A client that draws full-screen programs correctly. Coding agents use colours, box characters, spinners and constant redraws. Clients that handle the alternate screen and resizing badly turn that into noise.
  • Easy special keys. You will press Esc, Ctrl+C, Tab and the arrow keys often to interrupt, accept or move through choices. A toolbar with these keys beats hunting for them on an on-screen keyboard.
  • Sessions that survive a locked screen. You check, lock the phone, and check again an hour later without logging in and reattaching every time.

Termphin was built for this case: the terminal is drawn by the app rather than a web view, so agents and programs like htop and vim render correctly, the special-keys toolbar is yours to arrange, and its helper keeps the session alive across a locked screen or a network switch. It also has an SFTP file browser for a quick look at the files the agent changed.

Security checklist

  • A dedicated user without sudo for the agent's work.
  • Key-only SSH login, root login off, and a passphrase on the keys of your laptop and phone.
  • A separate key on the server for the git host, limited to the repositories it needs.
  • No production credentials or customer data on the machine. Treat it as something an automated tool controls.
  • Keep the agent's approval prompts on unless the machine is disposable.
  • Updates: sudo apt update && sudo apt upgrade regularly, or unattended upgrades.

Troubleshooting

"command not found" after npm install

npm's global folder is not on your PATH. Run npm prefix -g and add its bin subfolder to PATH in ~/.bashrc. With nvm, open a new shell or run source ~/.nvm/nvm.sh.

"EACCES: permission denied" during npm install -g

Global installs are trying to write to a system folder. Do not use sudo with npm; install Node.js with nvm, or set a prefix in your home folder with npm config set prefix ~/.npm-global and add ~/.npm-global/bin to PATH.

Garbled boxes or question marks on screen

The server's locale is not UTF-8 or TERM is wrong. Check locale and echo $TERM; inside tmux it should be tmux-256color or screen-256color, outside usually xterm-256color. Install a UTF-8 locale with sudo locale-gen en_US.UTF-8 on Debian and Ubuntu.

The agent stopped when I locked the phone

It was running directly in the SSH session. Start it inside tmux next time, or use a client that keeps the session alive. To find out what it finished, check git status and the log.

"Permission denied (publickey)" for the new user

The key is not in /home/dev/.ssh/authorized_keys or the permissions are too open. The folder must be 700, the file 600, both owned by dev.

FAQ

Can I run Claude Code or Codex on a server without a desktop?

Yes. Both are command-line programs that run in a terminal, so any Linux server you can reach over SSH works. The sign-in step may ask you to open a link in a browser on another device; follow the prompts the CLI prints.

Does the agent stop when my SSH connection drops?

If it runs directly in your SSH session, usually yes: the shell gets a hangup signal and its programs exit. Run it inside tmux or screen, or use a client that keeps the session alive on the server, and it carries on.

How big a server does a coding agent need?

The model runs at the provider, so the CLI itself is light. Size the server for your project instead: its build, tests and dependencies. A small VPS with 2 GB of memory is enough for many projects; heavy builds want more.

Should I open SSH to the internet for this?

You can, with key-only login and passwords turned off. A mesh VPN such as Tailscale or plain WireGuard lets you close port 22 to the public entirely, which removes the background noise of bots trying to log in.

Is it safe to let the agent run commands without asking?

Only in a place where its mistakes are cheap: a dedicated user without sudo, a disposable VM or container, and no production credentials. The approval prompts exist for a reason; flags that skip them trade safety for speed.

Why does the agent's screen look broken in my terminal?

Coding agents draw a full-screen interface with colours, box characters and redraws. A client that mishandles the alternate screen, Unicode or resizing garbles it. Check echo $TERM and your locale first, then try a different client.