---
title: "Self-host the Cloudroom core"
description: "Run your own Cloudroom core on an exe.dev VM and connect the Cloudroom app with a URL and token: Claude Code and Codex cloud threads, no API keys."
url: https://exe-staging.dev/deploy/cloudroom
category: "AI agents"
updated: 2026-10-31
---

# Self-host the Cloudroom core

Your own cloud for Cloudroom's agents, on a VM that never sleeps.

- **Software:** Cloudroom (https://www.cloudroom.dev)
- **Source:** https://github.com/davidondrej/cloudroom
- **License:** Apache-2.0
- **Time:** 10 minutes
- **Example VM name:** `my-cloudroom` (yours will differ; on the VM, `hostname` prints it)
- **Machine-readable:** https://exe-staging.dev/deploy/cloudroom/manifest.json

## What you get

- Cloudroom core running as a systemd service, back after a reboot, with its token in a root-only file
- Claude Code and Codex agents on exe.dev's LLM integration, with no API key on the VM
- Agents running as their own Linux account, without sudo
- The API on port 8000 for exe.dev's HTTPS proxy, answering only requests that carry the token
- A nightly backup of the core's history, settings and the agents' files, with a tested restore

Running on the VM:

- **Cloudroom app**: on your Mac or PC; Cloud threads run on the VM
- **Cloudroom core 0.1.89**: the cloudroom systemd service, listening only on 127.0.0.1:9840, data in `/var/lib/cloudroom/history, /etc/cloudroom`
- **nginx**: passes /v1/ on port 8000 to the core, for the HTTPS proxy, port 8000
- **Claude Code and Codex**: preinstalled; run as the cloudroom-agent account, on exe.dev's LLM integration, data in `/home/cloudroom-agent, /code`

How it is reached:

- https://my-cloudroom.exe.xyz/ (public): the core's API, once you make the VM public; every request needs the core's token
- http://localhost:9840/ (private): the same API through an SSH tunnel, if you'd rather keep the VM private

## Deploy with Shelley

Open https://exe-staging.dev/new?prompt=Set%20up%20Cloudroom%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe-staging.dev%2Fdeploy%2Fcloudroom.md%20from%20top%20to%20bottom.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-cloudroom.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-cloudroom.%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-%20Cloudroom%20core%20running%20as%20a%20systemd%20service%2C%20back%20after%20a%20reboot%2C%20with%20its%20token%20in%20a%20root-only%20file%0A-%20Claude%20Code%20and%20Codex%20agents%20on%20exe.dev%27s%20LLM%20integration%2C%20with%20no%20API%20key%20on%20the%20VM%0A-%20Agents%20running%20as%20their%20own%20Linux%20account%2C%20without%20sudo%0A-%20The%20API%20on%20port%208000%20for%20exe.dev%27s%20HTTPS%20proxy%2C%20answering%20only%20requests%20that%20carry%20the%20token%0A-%20A%20nightly%20backup%20of%20the%20core%27s%20history%2C%20settings%20and%20the%20agents%27%20files%2C%20with%20a%20tested%20restore%0A%0ARead%20the%20whole%20recipe%2C%20including%20the%20closed%20details.%20Run%20%22Install%20by%0Ahand%22%20only%2C%20skipping%20VM%20creation%20and%20SSH%3B%20don%27t%20run%20the%20restore%20or%0Aupdate%20instructions.%20Install%20exactly%20the%20pinned%20version.%0ANever%20print%2C%20log%20or%20send%20the%20core%27s%20token%2C%20and%20don%27t%20make%20the%20VM%20public%3A%0Athe%20owner%20decides%20that.%20On%20an%20existing%20VM%2C%20ask%20before%20replacing%20anything%0Aon%20port%208000.%0AVerify%20with%20the%20checks%20in%20each%20step%3A%20%2Fv1%2Fready%20through%20nginx%20says%0Aready%2C%20capabilities%20list%20claude-code%20and%20codex%2C%20the%20test%20task%20wrote%0Ahello.txt%2C%20and%20the%20backup%20timer%20is%20scheduled.%20Do%20not%20reboot.%0AFinish%20by%20telling%20the%20owner%20the%20two%20ways%20to%20connect%20the%20app%20from%0A%22Connect%20the%20app%22%2C%20and%20the%20command%20that%20prints%20the%20token.%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-staging.dev/deploy/cloudroom/prompt.txt | ssh exe.dev new --prompt=/dev/stdin
```

Or follow the steps below by hand.

[Cloudroom](https://www.cloudroom.dev) is a desktop app for coding agents:
Claude Code, Codex, Pi, OpenCode and others, side by side. Each thread runs
either on your computer or in the cloud, and **Teleport** moves a running
thread between the two. The cloud part is the Cloudroom core, an
open-source Rust service that runs the agents and records their history.
Cloudroom hosts it for you, or you run it on your own Linux machine.

This guide runs the core on an exe.dev VM that stays on when your laptop
closes. The agents run as their own Linux account without sudo, and reach
Claude and GPT models through exe.dev's
[LLM integration](/docs/integrations-llm), so there are no API keys to
paste. exe.dev's [HTTPS proxy](/docs/proxy) gives the core the HTTPS
address the app needs. Cloudroom's self-hosted core is an alpha for
Ubuntu 24.04, which is what exe.dev VMs run, and it changes daily.

## Connect the app

The app needs an HTTPS URL and the core's token. Get the token on your
computer:

```bash
ssh my-cloudroom.exe.xyz sudo sed -n s/^CLOUDROOM_TOKEN=//p /etc/cloudroom/core.env
```

Then pick how the app reaches the VM.

**From anywhere, over the public URL.** Make the VM's address public. Only
the core's API is there, and it refuses requests without the token:

```bash
ssh exe.dev share set-public my-cloudroom
```

In the app, open **Settings → Cloudroom account**, and under **Or connect
your own Core** enter `https://my-cloudroom.exe.xyz` and the token.

**Private, over SSH.** Leave the VM private and open a tunnel whenever you
use Cloud threads:

```bash
ssh -N -L 9840:localhost:9840 my-cloudroom.exe.xyz
```

Then connect the app to `http://localhost:9840` with the token. Agents
keep working when the tunnel closes; the app catches up when it's back.

The app says **Connected to your own Core**. You need the Cloudroom app
v83 or newer, on macOS, Windows (beta) or Linux (alpha).

> [!WARNING]
> Agents run without approval prompts and can read everything in
> `/home/cloudroom-agent` and `/code`, including each other's work. Give
> each trusted person their own VM and token, and keep unrelated secrets
> off this one.

## Start a Cloud thread

In the app, start a new thread, choose **Cloud**, pick **Claude Code** or
**Codex**, and ask for something small:

> Create a file hello.txt that says hello from the cloud, then show me
> its contents.

It runs on the VM, in a project folder under `/code`. Close the laptop: the thread
keeps going, and its history is waiting when you come back. **Teleport**
moves a thread between your computer and the VM with the same chat,
model and effort.

The agents can clone any repository the VM can reach. The
[GitHub integration](/docs/integrations-github) gives the VM access to
chosen private repositories without a token on its disk. A dev server an
agent starts on a port from 3000 to 9999 is at
`https://my-cloudroom.exe.xyz:<port>/`, behind your exe.dev login.

## Setup and upkeep

No need to read on unless you're curious—or doing it yourself.

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

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

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

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

### Install the core

Read [Cloudroom's installer](https://cloudroom.dev/install.sh) if you
like, then run it with a pinned version:

```bash
curl -fsSL https://cloudroom.dev/install.sh -o install.sh
sudo bash install.sh --version 0.1.89
```

In about 15 seconds it installs Node.js 24, creates the `cloudroom`
service account and the `cloudroom-agent` account the agents run as,
writes a new token to `/etc/cloudroom/core.env` (root only), and starts
the service on `127.0.0.1:9840`. Expect `Cloudroom core 0.1.89 is
running`.

### Connect the agents to the LLM integration

Set up Claude Code and Codex for the agent account, then tell the core
where Codex is. Claude Code needs no core settings: the core finds it
once `~/.claude` exists.

```bash
sudo -iu cloudroom-agent exeuntu configure claude
sudo -iu cloudroom-agent exeuntu configure codex
sudo tee -a /etc/cloudroom/core.env >/dev/null <<'EOF'
CLOUDROOM_CODEX_BINARY=/usr/local/bin/codex
CLOUDROOM_CODEX_HOME=/home/cloudroom-agent/.codex
CLOUDROOM_CODEX_MODEL=gpt-6.1-sol
EOF
sudo systemctl restart cloudroom
```

Check both, with a helper that keeps the token out of the command line:

```bash
api() {
  sudo awk -F= '/^CLOUDROOM_TOKEN=/{print "header = \"Authorization: Bearer " $2 "\""}' /etc/cloudroom/core.env |
    curl --config - --silent --show-error --fail-with-body "$@"
}
sleep 3
api http://127.0.0.1:9840/v1/capabilities | jq -r '.harnesses[].id'
```

Expect `codex` and `claude-code`.

### Run a test task

```bash
api --json '{"request_id":"setup-test","harness":"claude-code"}' http://127.0.0.1:9840/v1/sessions >/dev/null
sleep 10
api --json '{"request_id":"hello","text":"Create hello.txt containing Hello from Cloudroom."}' \
  http://127.0.0.1:9840/v1/sessions/cr_setup-test/prompts >/dev/null
sleep 30
sudo cat /home/cloudroom-agent/hello.txt
api --json '{"request_id":"close-setup-test"}' http://127.0.0.1:9840/v1/sessions/cr_setup-test/close >/dev/null
```

Expect `Hello from Cloudroom.` If the file isn't there yet, wait and
`cat` again.

### Put the API on port 8000

exe.dev's [HTTPS proxy](/docs/proxy) sends the VM's address to port 8000.
nginx passes the core's API there and nothing else, without buffering, so
the app's live event streams and terminals work. The core stays on
localhost:

```bash
sudo tee /etc/nginx/sites-available/cloudroom >/dev/null <<'EOF'
map $http_upgrade $connection_upgrade { default upgrade; '' ''; }
server {
    listen 8000;
    listen [::]:8000;
    client_max_body_size 0;
    location /v1/ {
        proxy_pass http://127.0.0.1:9840;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 1d;
        proxy_send_timeout 1d;
    }
    location / { return 404; }
}
EOF
sudo ln -sf /etc/nginx/sites-available/cloudroom /etc/nginx/sites-enabled/cloudroom
sudo nginx -t && sudo systemctl enable --now nginx && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/v1/ready
api http://127.0.0.1:8000/v1/ready
```

Expect `401` without the token, then `{"ready":true}` with it.

### Set up nightly backups

Each night at about 3:30 UTC, a timer archives the core's settings and
history and the agents' home and `/code` to `/var/backups/cloudroom`,
keeping two weeks:

```bash
sudo tee /etc/systemd/system/cloudroom-backup.service >/dev/null <<'EOF'
[Unit]
Description=Back up Cloudroom core

[Service]
Type=oneshot
UMask=0077
ExecStart=/bin/sh -c 'mkdir -p /var/backups/cloudroom && tar -czf /var/backups/cloudroom/cloudroom-$$(date +%%Y%%m%%d-%%H%%M).tar.gz -C / etc/cloudroom var/lib/cloudroom home/cloudroom-agent code && find /var/backups/cloudroom -name "cloudroom-*.tar.gz" -mtime +13 -delete'
EOF
sudo tee /etc/systemd/system/cloudroom-backup.timer >/dev/null <<'EOF'
[Unit]
Description=Nightly Cloudroom backup

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

[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now cloudroom-backup.timer
sudo systemctl start cloudroom-backup.service
sudo sh -c 'tar -tzf "$(ls -t /var/backups/cloudroom/cloudroom-*.tar.gz | head -1)"' | grep -c core.env
systemctl list-timers cloudroom-backup.timer
```

Expect `1` and a scheduled next run. Then
[connect the app](#connect-the-app).

</details>

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

Everything lives in four places: `/etc/cloudroom` (settings and token),
`/var/lib/cloudroom` (history), `/home/cloudroom-agent` (the agents'
logins and settings) and `/code` (the agents' work). On the VM, take an
archive now:

```bash
sudo systemctl start cloudroom-backup.service
sudo sh -c 'ls -t /var/backups/cloudroom/cloudroom-*.tar.gz | head -3'
```

The `auto-` folders beside them are the old binaries Cloudroom's updater
keeps for its rollbacks.

Copy archives **off the VM** too, and keep them private: they hold the
token and everything the agents wrote. On your computer:

```bash
ssh my-cloudroom.exe.xyz 'sudo sh -c "cat \$(ls -t /var/backups/cloudroom/cloudroom-*.tar.gz | head -1)"' > cloudroom-backup.tar.gz
```

To restore on the VM, replace `BACKUP.tar.gz` with the archive's name.
On a new VM, run the install steps first, then restore over them:

```bash
sudo systemctl stop cloudroom &&
sudo tar -xzf /var/backups/cloudroom/BACKUP.tar.gz -C / &&
sudo systemctl start cloudroom
```

The app's threads and the agents' files come back, with the old token.
Pushing work to Git as you go is still the best backup for code.

</details>

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

Cloudroom publishes core releases almost daily. Update the core and the
app together. Pick a `core-v` version from the
[releases](https://github.com/davidondrej/cloudroom/releases), take a
backup, then, replacing `X.Y.Z`:

```bash
sudo systemctl start cloudroom-backup.service
cd ~ && v=X.Y.Z && a=$(uname -m)
curl -fsSLO https://github.com/davidondrej/cloudroom/releases/download/core-v$v/cloudroom-core-$v-linux-$a.tar.gz
mkdir core-$v && tar -xzf cloudroom-core-$v-linux-$a.tar.gz -C core-$v
sudo python3 core-$v/install/update.py upgrade "$PWD/core-$v"
sudo cat /var/lib/cloudroom-update/state.json
```

The updater only swaps the binary after 10 minutes without agent
activity, then checks every session and rolls back on failure. It never
downgrades. `"result": "updated"` means done. `waiting` means agents
worked recently: run the last two commands again in 15 minutes (until
then it answers `Waiting before the next attempt.`). A note
`previews: CalledProcessError` is harmless: previews aren't set up on a
self-hosted core.

</details>

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

Ask Shelley, or check on the VM:

```bash
systemctl is-active cloudroom nginx
sudo journalctl -u cloudroom -n 50 --no-pager
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/v1/ready
```

- **The app can't connect?** On the public URL, check the VM is public
  (`ssh exe.dev share show my-cloudroom`) and that you pasted the whole
  token. `https://my-cloudroom.exe.xyz/v1/ready` should answer
  `{"error":"unauthorized"}` in a browser: that's the core, without the
  token. A login page means the VM is still private.
- **`connect Claude Code before starting cloud work`?**
  `/home/cloudroom-agent/.claude/settings.json` is missing. Run
  `sudo -iu cloudroom-agent exeuntu configure claude` and restart the
  core.
- **Codex isn't offered?** The three `CLOUDROOM_CODEX_` lines are missing
  from `/etc/cloudroom/core.env`. Add them and restart the core.
- **An agent fails at once?** Check the
  [LLM integration](/docs/integrations-llm):
  `curl -s https://llm.int.exe.xyz/v1/models | jq -r '.data[].id'`.
- **Token leaked?** Put a new one in place and reconnect the app:

  ```bash
  sudo sed -i "s/^CLOUDROOM_TOKEN=.*/CLOUDROOM_TOKEN=$(openssl rand -hex 32)/" /etc/cloudroom/core.env
  sudo systemctl restart cloudroom
  ```

- **Disk filling up?** The core warns agents below 5 GB free. Check
  `df -h /`, delete old folders under `/code`, or grow the disk with
  `ssh exe.dev resize my-cloudroom --disk=100GB`.

</details>

## Frequently asked questions

### What does it cost?

Cloudroom is free and Apache-2.0 licensed, and a self-hosted core needs no Cloudroom account. You pay for your [VM plan](https://exe.dev/pricing) and the agents' model usage through the [LLM integration](/docs/integrations-llm), or use your own Claude or ChatGPT subscription.

### Is a public URL safe?

The core refuses every request without its 64-character token, and nginx answers 404 for anything outside `/v1/`. The token is as good as a shell on the VM for anyone who holds it, so keep it in a password manager and [change it](#troubleshooting) if it leaks. To keep the VM private instead, use the SSH tunnel. The app can't sign in to exe.dev, so it can't pass the proxy's login on a private VM.

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

The core supports both: in the app, with Cloud selected, use **Connect Claude** or **Connect Codex**. First remove the gateway settings that would win over your login: on the VM, `sudo rm /home/cloudroom-agent/.claude/settings.json` for Claude, or delete the `exe-llm` lines from `/home/cloudroom-agent/.codex/config.toml` for Codex, then `sudo systemctl restart cloudroom`. We tested the gateway, not subscription logins.

### How many agents can one VM run?

The core sets no limit; memory and CPU do. A new VM has 2 vCPUs and 8 GB, enough for a handful of agents. Each Claude Code or Codex process takes a few hundred MB before it builds or tests anything. Give the VM more with `ssh exe.dev resize my-cloudroom --cpu=8 --memory=32GB`, up to your plan's capacity, or run several cores on several VMs.

### How is this different from hosted Cloudroom?

Hosted Cloudroom gives each Cloud thread its own sandbox. A self-hosted core runs every Cloud thread on this one VM, in folders under `/code`, so threads share its disk and its tools. Features that need Cloudroom's own sandboxes, such as GPUs and previews, aren't set up here.

### Which agents can it run?

This guide sets up Claude Code and Codex. The core also runs Pi, OpenCode, Cursor, Droid, OpenClaw and Hermes once each is installed and signed in under the `cloudroom-agent` account; Cloudroom's [harness docs](https://github.com/davidondrej/cloudroom/blob/main/core/docs/harnesses.md) say what each needs.


## Related

- [Self-host Paperclip](https://exe-staging.dev/deploy/paperclip.md): Hire a team of AI agents. Give them an office that never closes.
- [Run OpenRig on a remote server](https://exe-staging.dev/deploy/openrig.md): A team of coding agents that keeps its seats when you log off.
- [Run Orca's agents on a remote server](https://exe-staging.dev/deploy/orca.md): A big, always-on box for Orca's parallel coding agents.
- [Run Claude Code on a remote server](https://exe-staging.dev/deploy/claude-code.md): A computer for Claude. A breather for your laptop.
- [Run Codex on a remote server](https://exe-staging.dev/deploy/codex.md): A computer for Codex. An early night for your laptop.
- [Host OpenClaw](https://exe-staging.dev/deploy/openclaw.md): An AI sidekick with its own computer. Your laptop can take a nap.
