Claude Code on a VPS, reachable from your phone
A coding agent is most useful when it keeps working after you close the laptop. A $5 VPS gives
it a machine that never sleeps; Pidge gives it a way to reach you — and you a way to reach it —
without an SSH session open on your phone. This guide sets both up on a fresh Ubuntu box: Claude
Code inside tmux, a real notification when a task finishes or needs a call, your answer from
the lock screen flowing back, and your messages picked up whenever the agent is listening.
Why a VPS instead of your laptop
Section titled “Why a VPS instead of your laptop”- It runs while you don’t. A long refactor, a test matrix, a nightly job — none of it depends on your Mac being awake or on the same Wi-Fi.
- The phone becomes the control surface. You don’t watch a terminal; the agent tells you when something needs you, with the urgency it deserves, and your answer flows back.
- It is cheap and disposable. One small box per project is fine; the Pidge key is scoped per project, so boxes never collide.
1. Prepare the box
Section titled “1. Prepare the box”Start from any Ubuntu 24.04 image. Create a non-root user for the agent, and do everything else as that user:
adduser agentusermod -aG sudo agent # optional; the agent itself never needs sudosu - agentInstall tmux. This is the one non-negotiable piece: an SSH drop must not kill the session.
sudo apt-get update && sudo apt-get install -y tmux git build-essentialtmux new -s claude # start the session you will keep forever# … work …# detach: Ctrl-b then d · come back later: tmux attach -t claudeInstall Node. Node 22 or newer is what you want: pidge-cli’s realtime path (the WebSocket that delivers your answer to the agent in under a second) needs it. Node 18 works too — the CLI degrades to long-polling on its own — it is just slower to hear you. Use your distro’s NodeSource packages or a version manager such as mise if you also want Ruby, Python or Go on the same box.
Install Claude Code per Anthropic’s current instructions and sign in once. Then clone your project and start Claude Code inside the tmux session.
2. Give the agent a channel
Section titled “2. Give the agent a channel”In the Pidge app on your iPhone: Channels → New channel, name it after the project, and tap Copy setup prompt. The prompt carries a single-use claim code with a 15-minute TTL — never the key. Paste the whole prompt into Claude Code; the agent runs the setup itself. If you prefer to do it by hand, run this from inside the project folder on the server:
npx -y pidge-cli@latest setup --claim <code> --url https://api.pidge.shsetup exchanges the code for the channel key, writes it to a config file scoped to this
project (~/.config/pidge/projects/<hash>/env, mode 600), runs a health check, and writes the
Pidge skill into .claude/skills/pidge/SKILL.md so the session knows when and how to reach you.
The key never appears in the chat or on screen. Claiming again later rotates the key and cuts the
previous holder off — that is the revocation mechanism, not a bug.
Two commands prove the wiring:
npx -y pidge-cli@latest doctor # env source, server, key, "channel X · N devices", realtime probenpx -y pidge-cli@latest hello # sends, then waits up to ~2 min for your tap; a timeout is normalhello narrates a three-stage Live Activity on your lock screen and waits for you to confirm.
If you don’t tap in time, nothing is lost — the reply lands in the agent’s queue.
3. Let the agent reach you at the finish line
Section titled “3. Let the agent reach you at the finish line”The pattern that pays for itself: a long task ends, the agent reports with substance, and when a decision is needed it blocks on your answer instead of printing a question into a terminal nobody is watching.
# done, no decision needed — a pendency card that stays until you resolve it:npx pidge-cli important --title "Migration finished" --body-markdown "$(cat summary.md)"
# done, needs a call — blocks until you answer, then prints your choice as JSON:npx pidge-cli ask --title "Tests green. Ship it?" --actions yes,no --timeout 14400Give long waits a generous --timeout. Exit code 3 means “no answer yet”, not a failure;
the agent can re-wait on the correlation id later instead of sending again. Pick the type by
weight: message for a pure FYI, important for anything you should act on, urgent only
when losing minutes costs something — your channel’s ceiling decides how loud it may get.
4. Let yourself reach the agent
Section titled “4. Let yourself reach the agent”This is where the VPS setup stops being a pager and starts being a messenger. Every channel is a thread: type a message in the app, dictate it, drop a photo or a file, and it waits in the agent’s queue until the agent is on the line. Under Claude Code, the agent stays on the line for the whole session with a watch the harness owns:
npx pidge-cli online --follow --ndjson --timeout 0 # one message per line, no deadline — run under Claude Code's Monitor toolIf setup ran under Claude Code, it also installed a SessionStart hook that runs pidge presence
on startup, resume, /clear and /compact — one line telling the agent whether the watch is up,
so a session born from zero remembers to start it. Each message that arrives is handled,
answered through pidge message, and then acked:
npx pidge-cli ack --up-to <id> --summary "<what you did>" # only after the work is durably done → ✓✓ greenNo such watch in your harness (a plain shell, another agent CLI)? Run one round at a time in the foreground and run it again when it returns — you are online while you keep calling:
npx pidge-cli online # blocks until you write (or answer an earlier ask), prints JSON, exitsIt works like any messenger: what you send waits until the agent is on the line, and the checkmarks tell you exactly where it is — delivered when the agent read it, green once it actually handled it. An un-acked message is re-served when its ~10-minute lease expires, so an agent that dies mid-task never loses one. When the session ends, you see the agent offline in the app, like a contact who closed the chat; your messages wait in the queue and reach it when it is back.
5. A stand-in while you’re away (optional)
Section titled “5. A stand-in while you’re away (optional)”When the session ends, the agent is offline — and many people prefer exactly that: if you are
gone, the app says so. If you do want someone answering in your place around the clock, install
a stand-in: a supervised daemon that runs a separate model session per batch of messages,
answers through pidge message, and yields the channel back the moment the live agent is on
the line again.
npx pidge-cli bridge install --handler claude --enable # from the project folder; --enable starts it and proves it with a selftestnpx pidge-cli bridge status # the measured verdict: service · lock · servernpx pidge-cli bridge uninstall # stop and remove itIt is a systemd --user unit on Linux; on a headless box enable lingering so it runs with nobody
logged in (sudo loginctl enable-linger agent). The handler and its prompt are generated as
editable files next to the config, and the key is never embedded. The full contract — batches,
leases, the handoff between the stand-in and a live session — is in
Waiting & receiving.
6. Optional: a Face ID gate on the scary commands
Section titled “6. Optional: a Face ID gate on the scary commands”If the agent can run migrations or touch production, pidge approve "<question>" is a
deny-default exit-code gate made for a Claude Code PreToolUse hook — exit 0 only on an
explicit Face ID allow. The recipe is in Approval gates.
Does the agent have to be running for my message to arrive? No. The message is stored in the channel’s queue the moment you send it and delivered the next time something listens — the session’s watch, or a stand-in bridge if you installed one. The ✓✓ show you when that happened.
What happens when the VPS reboots?
tmux sessions don’t survive a reboot, but nothing in Pidge is lost: queued messages wait, and a
bridge install unit comes back with systemd (with lingering enabled). Start tmux and Claude
Code again; the per-project key is still on disk, so no re-claim is needed.
Do I need to open any ports or run a web server?
No. The CLI only makes outbound HTTPS and WebSocket connections to api.pidge.sh; nothing
listens on the box, and the firewall can stay closed. Pidge is iPhone-first today, with a Mac
app on the way.
