---
title: "Run Pi Pocket on a remote server"
description: "Host Pi Pocket, the mobile-first multiplayer web app for Pi agents, on an exe.dev VM: private HTTPS for your phone, models without API keys, durable runs."
url: https://exe.dev/deploy/pi-pocket
category: "AI agents"
updated: 2026-10-31
---

# Run Pi Pocket on a remote server

Your Pi agent on your phone, running on a VM that never sleeps.

- **Software:** Pi Pocket (https://tannermidd.github.io/pi-pocket/)
- **Source:** https://github.com/TannerMidd/pi-pocket
- **License:** MIT
- **Time:** 10 minutes
- **Example VM name:** `my-pi-pocket` (yours will differ; on the VM, `hostname` prints it)
- **Machine-readable:** https://exe.dev/deploy/pi-pocket/manifest.json

## What you get

- Pi Pocket at `https://<vm>.exe.xyz/`, behind exe.dev's login, restarted by systemd after a crash or reboot.
- Claude and GPT models through exe.dev's LLM integration. No API key on the VM.
- The owner's sign-in link, read from Pi Pocket's settings rather than its log.
- Nightly backups of Pi Pocket's and Pi's settings, and one checked to hold the database.

Running on the VM:

- **Pi Pocket 0.27**: npm package on Node.js 24, managed by systemd, port 8000, data in `~/.pi-pocket`
- **Pi's settings**: Models through exe.dev's LLM integration, sign-ins, skills, data in `~/.pi/agent`
- **Your projects**: Where new sessions start, data in `~/projects`
- **Nightly backups**: Pi Pocket's database and settings, and Pi's; 14 kept, data in `~/pi-pocket-backups`

How it is reached:

- https://my-pi-pocket.exe.xyz/ (private): Pi Pocket, behind your exe.dev login and Pi Pocket's own sign-in link
- https://my-pi-pocket.exe.xyz/ (shared): People you share the VM with and invite from the app

## Deploy with Shelley

Open https://exe.dev/new?prompt=Set%20up%20Pi%20Pocket%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe.dev%2Fdeploy%2Fpi-pocket.md%20from%20top%20to%20bottom.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-pi-pocket.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-pi-pocket.%20Skip%20the%20step%20that%20creates%20the%20VM%3B%20you%20are%20on%20it.%0A%0AYou%20are%20done%20when%20all%20of%20these%20are%20true.%20Check%20each%20one%20yourself%3A%0A-%20Pi%20Pocket%20at%20%60https%3A%2F%2F%3Cvm%3E.exe.xyz%2F%60%2C%20behind%20exe.dev%27s%20login%2C%20restarted%20by%20systemd%20after%20a%20crash%20or%20reboot.%0A-%20Claude%20and%20GPT%20models%20through%20exe.dev%27s%20LLM%20integration.%20No%20API%20key%20on%20the%20VM.%0A-%20The%20owner%27s%20sign-in%20link%2C%20read%20from%20Pi%20Pocket%27s%20settings%20rather%20than%20its%20log.%0A-%20Nightly%20backups%20of%20Pi%20Pocket%27s%20and%20Pi%27s%20settings%2C%20and%20one%20checked%20to%20hold%20the%20database.%0A%0ARead%20the%20whole%20recipe%2C%20including%20the%20closed%20details.%20Follow%20%22Install%20by%0Ahand%22%20first%2C%20skipping%20VM%20creation%3B%20the%20walkthrough%20is%20for%20the%20owner%0Aafterwards.%20Do%20not%20run%20the%20later%20Restore%20or%20Update%20instructions.%0AKeep%20the%20VM%20private%3A%20never%20run%20share%20set-public%2C%20because%20whoever%20can%0Asteer%20a%20session%20can%20run%20commands%20with%20sudo.%20On%20an%20existing%20VM%2C%20ask%0Abefore%20replacing%20~%2F.pi%2Fagent%2Fmodels.json%20or%20anything%20that%20listens%20on%0Aport%208000.%20Don%27t%20start%20an%20interactive%20pi%20or%20a%20Pi%20Pocket%20session.%0AVerify%20that%20pi-pocket.service%20and%20pi-pocket-backup.timer%20are%20enabled%2C%0Athat%20%2Fapi%2Fproviders%20lists%20anthropic%20and%20openai%20as%20configured%2C%20the%20pi%0AOK%20reply%2C%20and%20a%20backup%20that%20holds%20.pi-pocket%2Fpocket.sqlite.%0AFinish%20with%20the%20owner%27s%20sign-in%20link%20from%20%22Sign%20in%22%20%28it%27s%20a%20key%3A%20send%20it%0Aonly%20to%20the%20owner%2C%20here%29%2C%20and%20tell%20them%20to%20sign%20in%20to%20exe.dev%20when%20asked%2C%0Athen%20pick%20a%20model%20from%20the%20model%20chip%20in%20their%20first%20session.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open.%0A to create a VM whose agent, Shelley, follows this recipe. From a terminal:

```sh
curl -fsSL https://exe.dev/deploy/pi-pocket/prompt.txt | ssh exe.dev new --prompt=/dev/stdin
```

Or follow the steps below by hand.

[Pi Pocket](https://github.com/TannerMidd/pi-pocket) puts the
[Pi](/deploy/pi) coding agent in a web app made for phones: sessions
you start from the couch, push notifications when Pi finishes or wants
approval, dictation, and a home-screen icon. It's multiplayer, too:
invite people to watch or steer the same session live, with a side chat
Pi doesn't see. Underneath is [Pi Durable](/deploy/pi-durable), so a run
survives the server restarting halfway through.

Pi Pocket runs on a machine of your own, not a hosted service, and your
phone reaches it through a tunnel or Tailscale. An exe.dev VM is a machine
of your own that doesn't sleep or close its lid, with an HTTPS address behind your exe.dev login, so
notifications and the home-screen app work without a tunnel. Models come
from exe.dev's [LLM integration](/docs/integrations-llm), so there's no
API key to paste, and Pi gets a machine of its own to run commands on.

> [!NOTE]
> Pi Pocket is new and changes daily. This page installs 0.27, the
> current release, and was checked with 0.27.0.

## Sign in

Pi Pocket's owner link is the key to the server. On your computer, print
it:

```bash
ssh my-pi-pocket.exe.xyz 'echo "https://$(hostname).exe.xyz/login?token=$(jq -r .ownerToken ~/.pi-pocket/config.json)"'
```

Open it on your computer and sign in to exe.dev if asked. Keep the link
private; anyone who has it, and can get past exe.dev's login, is you.

Tap **New**, keep the `projects` folder, and **Open**. The model chip in
the message box says *no model*: tap it and pick one, such as **Claude
Sonnet 5.5**. New sessions start with the last model picked. Then try:

> Look around this VM and describe it in three lines.

To see the durable part, ask Pi to count to 30 with bash, one number a
second, and restart Pi Pocket while it counts. On your computer:

```bash
ssh my-pi-pocket.exe.xyz sudo systemctl restart pi-pocket
```

The command shows as *cut off by a restart*, and Pi carries on from there.

## Put it in your pocket

On your computer, open the rail's **People** (two heads) → **Invite
someone**, tick **Sign in as me (owner)**, and scan the QR code with your
phone. Sign in to exe.dev on the phone when it asks; the invite lasts 15
minutes, which is plenty.

<figure>
<img src="https://exe.dev/deploy/pi-pocket/phone.png" alt="Pi Pocket on a phone: Pi ran one bash command on the VM and reports Ubuntu 24.04, 2 CPUs, a 25 GB disk, and the hello.txt it created" width="780" height="1688" loading="lazy" style="max-width: 390px; height: auto">
<figcaption>A session on a phone. Pi ran on the VM, with Claude Sonnet 5.5 through exe.dev.</figcaption>
</figure>

Then, on the phone:

- **Add to Home Screen** from the browser's share menu, for a full-screen
  app.
- Menu → **Notifications** to get a push when Pi finishes, needs an
  approval, or someone mentions you. They need HTTPS, which the VM's
  address already has.

## Bring people in

On your computer, let them past exe.dev's login:

```bash
ssh exe.dev share add my-pi-pocket ann@example.com
```

Then, in Pi Pocket, **People** → **Invite someone**: choose **Steer** or
**View only**, how long the invite lasts, and send Ann the link. She opens
it, signs in to exe.dev, and picks a name. Everyone in a session sees
who's there, and Pi's work, live.

> [!WARNING]
> Anyone who can steer can make Pi run any command on this VM, with sudo.
> Invite only people you'd hand a terminal to, use **View only** for the
> rest, and never make the VM public.

## Keep it safe

Everything that isn't your code is in two folders on the VM:
`~/.pi-pocket` (sessions, people, settings, uploads) and `~/.pi/agent`
(Pi's models and sign-ins). Each night between 03:30 and 04:00 UTC, Pi
Pocket stops for a few seconds while both are copied to
`~/pi-pocket-backups`; runs in progress pick up again. The backups hold
the owner token, so copy them somewhere private, **off the VM**. On your
computer:

```bash
rsync -av my-pi-pocket.exe.xyz:pi-pocket-backups/ ./pi-pocket-backups/
```

Your code in `~/projects` isn't in them: push it to a Git remote, or copy
the whole VM with `ssh exe.dev cp my-pi-pocket my-pi-pocket-copy`.

## The nuts and bolts

Happy clicking Deploy? You can stop here. These are the setup steps
Shelley follows, plus the commands for later.

<details id="create-the-vm" data-manual-setup class="dp-details">
<summary>Install by hand</summary>

### Create a VM

On your computer (pick another name if `my-pi-pocket` is taken):

```bash
ssh exe.dev new --name=my-pi-pocket
ssh my-pi-pocket.exe.xyz
```

All remaining installation commands run **on the VM**, as `exedev`.

### Install Node.js 24

Pi Pocket needs Node.js 22.19 or newer. NodeSource's package installs
Node.js 24:

```bash
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version
```

Expect `v24.22.1` or later.

### Install Pi Pocket

Install it into `~/.local`, a folder `exedev` owns, so Pi Pocket can
update itself from its menu later:

```bash
npm install -g --prefix ~/.local --no-audit --no-fund pi-pocket@0.27
~/.local/bin/pi-pocket --help | head -1
```

The help starts `Pi Pocket: a durable, multiplayer, mobile-first web app
for Pi agents.` npm warns that `@google/genai` and `protobufjs` have
install scripts it didn't run; Pi Pocket needs neither.

### Connect the models

Pi Pocket uses Pi's own model settings in `~/.pi/agent`. This file sends
Pi's Anthropic and OpenAI models to exe.dev's
[LLM integration](/docs/integrations-llm), which adds the credentials on
its way out; the key in it is a placeholder. The Pi command line on this
VM reads the same file:

```bash
mkdir -p ~/.pi/agent ~/projects
[ -s ~/.pi/agent/models.json ] || cat > ~/.pi/agent/models.json <<'EOF'
{
  "providers": {
    "anthropic": { "baseUrl": "https://llm.int.exe.xyz/anthropic", "apiKey": "exe-llm-integration" },
    "openai": { "baseUrl": "https://llm.int.exe.xyz/openai/v1", "apiKey": "exe-llm-integration" }
  }
}
EOF
cd ~/projects && pi -p --model anthropic/claude-haiku-5-5 "Reply with exactly: OK" </dev/null
```

Expect `OK`. If `models.json` already existed, the command left it
alone: add the two providers to it yourself.

### Start it

systemd starts Pi Pocket at boot and restarts it if it stops. It listens
on port 8000, so it's at `https://<vm>.exe.xyz/`; `--access lan` makes it
listen on the VM's network address, where exe.dev's
[HTTPS proxy](/docs/proxy) reaches it:

```bash
sudo tee /etc/systemd/system/pi-pocket.service >/dev/null <<'EOF'
[Unit]
Description=Pi Pocket
Wants=network-online.target
After=network-online.target

[Service]
User=exedev
WorkingDirectory=/home/exedev/projects
Environment=PATH=/home/exedev/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/home/exedev/.local/bin/pi-pocket --access lan --port 8000 --cwd /home/exedev/projects
Restart=always
RestartSec=3
KillMode=mixed
TimeoutStopSec=20

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pi-pocket
until curl -fso /dev/null localhost:8000/style.css; do sleep 1; done
systemctl is-enabled pi-pocket
```

Expect `enabled`. Check that Pi Pocket sees the models, using the owner
token:

```bash
curl -fsS -H "Authorization: Bearer $(jq -r .ownerToken ~/.pi-pocket/config.json)" \
  localhost:8000/api/providers | jq -r '.[] | select(.configured) | .id'
```

Expect `anthropic` and `openai`.

### Set up backups

The backup script stops Pi Pocket for a few seconds, so the database is
copied whole, and packs `~/.pi-pocket` and `~/.pi/agent`. It leaves out
Pi's own program files and exe.dev's Pi extension, which come with the VM:

```bash
mkdir -p ~/pi-pocket-backups
sudo tee /usr/local/bin/pi-pocket-backup >/dev/null <<'EOF'
#!/bin/sh
# Back up Pi Pocket (~/.pi-pocket) and Pi's settings (~/.pi/agent) to
# ~/pi-pocket-backups/pi-pocket-<UTC time>.tar.gz. Keeps the newest 14.
# Pi Pocket stops while the database is copied; its runs resume after.
set -eu
cd /home/exedev
dest=pi-pocket-backups/pi-pocket-$(date -u +%Y%m%d-%H%M%S).tar.gz
systemctl stop pi-pocket
trap 'systemctl start pi-pocket' EXIT
tar -czf "$dest.partial" --exclude=.pi/agent/bin --exclude=.pi/agent/extensions/exe-dev .pi-pocket .pi/agent
systemctl start pi-pocket
trap - EXIT
chmod 600 "$dest.partial"
chown exedev: "$dest.partial"
mv "$dest.partial" "$dest"
ls -1 pi-pocket-backups/pi-pocket-*.tar.gz | head -n -14 | xargs -r rm --
echo "/home/exedev/$dest"
EOF
sudo chmod 755 /usr/local/bin/pi-pocket-backup
```

Take one and look inside:

```bash
backup=$(sudo pi-pocket-backup)
tar -tzf "$backup" | grep -E 'pocket.sqlite$|config.json$|models.json$'
```

Expect `.pi-pocket/pocket.sqlite`, `.pi-pocket/config.json`, and
`.pi/agent/models.json`. Then back up every night:

```bash
sudo tee /etc/systemd/system/pi-pocket-backup.service >/dev/null <<'EOF'
[Unit]
Description=Back up Pi Pocket

[Service]
Type=oneshot
ExecStart=/usr/local/bin/pi-pocket-backup
EOF
sudo tee /etc/systemd/system/pi-pocket-backup.timer >/dev/null <<'EOF'
[Unit]
Description=Back up Pi Pocket every night

[Timer]
OnCalendar=*-*-* 03:30:00 UTC
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pi-pocket-backup.timer
systemctl list-timers pi-pocket-backup.timer
```

The timer should list its next run. Installation done: [sign in](#sign-in).

</details>

<details id="back-up-and-restore" class="dp-details">
<summary>Back up and restore</summary>

On the VM, `sudo pi-pocket-backup` takes an extra backup and prints its
path. Take one before every update.

### Go back to a backup

This replaces sessions, people, settings and Pi's sign-ins with the
backup's, and loses everything since. On the VM:

```bash
ls ~/pi-pocket-backups
sudo systemctl stop pi-pocket
rm -rf ~/.pi-pocket
tar -xzf ~/pi-pocket-backups/pi-pocket-20261031-033012.tar.gz -C ~
sudo systemctl start pi-pocket
```

Use a file from the listing in place of the example. The owner link is
the backup's, so signed-in browsers stay signed in.

### Move to a new VM

On the new VM, follow [Install by hand](#create-the-vm) through **Install
Pi Pocket**. Copy a backup across from your computer, for example
`scp ./pi-pocket-backups/pi-pocket-20261031-033012.tar.gz my-new-vm.exe.xyz:`,
then unpack it on the new VM with `mkdir -p ~/projects && tar -xzf
~/pi-pocket-20261031-033012.tar.gz -C ~`, and follow **Start it** and
**Set up backups**. Copy your projects across too. Stop the old one first,
on the old VM: `sudo systemctl disable --now pi-pocket pi-pocket-backup.timer`.
The sign-in link now starts with the new VM's address, and everyone signs
in there again.

</details>

<details id="update" class="dp-details">
<summary>Update Pi Pocket</summary>

Read the [release notes](https://github.com/TannerMidd/pi-pocket/releases)
first: some versions change the database for good, and then only the
backup takes you back. Menu → **Update Pi Pocket** installs a new release
and restarts it, going back by itself if it doesn't start. Or by hand, on
the VM:

```bash
sudo pi-pocket-backup
npm install -g --prefix ~/.local --no-audit --no-fund pi-pocket@latest
sudo systemctl restart pi-pocket
journalctl -u pi-pocket -n 30 --no-pager | grep -F 'Pi Pocket'
```

The log should end with `Pi Pocket  ● running`. Betas (`pi-pocket@beta`)
come out almost daily; they're tested less, and can change stored data.

</details>

<details id="troubleshooting" class="dp-details">
<summary>Something not working?</summary>

On the VM:

```bash
systemctl status pi-pocket --no-pager
journalctl -u pi-pocket -n 50 --no-pager
```

- **The model picker says *No models are available*:** check
  `~/.pi/agent/models.json` (`jq . ~/.pi/agent/models.json` shows a typo),
  then restart Pi Pocket. `curl https://llm.int.exe.xyz/v1/models` lists
  what the [LLM integration](/docs/integrations-llm) offers this VM.
- **A model fails with a sign-in error:** that provider has a line in
  `models.json` *and* your own sign-in. Keep one of the two (see the FAQ).
- **The sign-in page keeps coming back:** Pi Pocket's cookie belongs to
  one address. Use `https://my-pi-pocket.exe.xyz/`, not the `http://10…`
  address the log prints.
- **Signed out everywhere, or a link leaked:** stop the service, run
  `~/.local/bin/pi-pocket --rotate-token --access lan --port 8000` once
  and press **q** when it's up, then start the service. Every device that
  used the old link must sign in again.
- **`EADDRINUSE` in the log:** something else listens on port 8000.

</details>

## Frequently asked questions

### Why does Pi Pocket ask me to sign in after exe.dev already did?

They're two locks. exe.dev's [HTTPS proxy](/docs/proxy) decides who reaches the VM at all; Pi Pocket's sign-in link or invite decides who you are inside it, and what you may do (owner, steer, or view only). Pi Pocket doesn't read exe.dev's `X-ExeDev-Email` header, so each browser signs in to it once. Its cookie lasts a year.

### Can I use my Claude or ChatGPT subscription instead?

Yes, from the Providers sheet. First delete that provider's line from `~/.pi/agent/models.json` on the VM and run `sudo systemctl restart pi-pocket`: the line sends every request for that provider to exe.dev's LLM integration, your own sign-in included.

### How is this different from Pi and Pi Durable?

[Pi](/deploy/pi) is the terminal coding agent, preinstalled on exe.dev VMs. [Pi Durable](/deploy/pi-durable) is Earendil's library for agents that survive a crash. Pi Pocket is an independent web app, by Tanner Middleton, that runs Pi on Pi Durable and adds what a phone needs: a touch UI, notifications, and other people in the same session.

### Is it stable?

It's young and moving fast: a release or beta most days since it started in October 2026, and Pi Durable is experimental. Some updates change the database one way, so [take a backup](#back-up-and-restore) before every update. This page was checked with 0.27.0.

### What does it cost?

Your [VM plan](https://exe.dev/pricing) plus model usage, billed to your exe.dev account through the [LLM integration](/docs/integrations-llm). Pi Pocket shows each session's spend under the message box.

### Does the Browser panel work?

Not as installed here. It needs Chromium, Chrome, Brave or Edge on the VM, which this recipe doesn't install. The rest of the app doesn't need it.


## Related

- [Host a Pi Durable agent](https://exe.dev/deploy/pi-durable.md): An agent that picks up where it left off, even after kill -9.
- [Run the Pi coding agent on a remote server](https://exe.dev/deploy/pi.md): An agent that doesn't ask first, on a computer that can take it.
- [Run OpenCode on a remote server](https://exe.dev/deploy/opencode.md): Every model, one VM, and an agent that works while your laptop sleeps.
- [Run T3 Code on a remote server](https://exe.dev/deploy/t3code.md): Your coding agents on an always-on VM, steered from your phone.
- [Run Claude Code on a remote server](https://exe.dev/deploy/claude-code.md): A computer for Claude. A breather for your laptop.
- [Host OpenClaw](https://exe.dev/deploy/openclaw.md): An AI sidekick with its own computer. Your laptop can take a nap.
