Self-host the Cloudroom core
Your own cloud for Cloudroom's agents, on a VM that never sleeps.
Shelley handles the setup. About 10 minutes.
Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/cloudroom/prompt.txt | ssh exe.dev new --prompt=/dev/stdinCloudroom 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, so there are no API keys to paste. exe.dev's HTTPS 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.
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
What's running, and where
https://my-cloudroom.exe.xyz/
the core's API, once you make the VM public; every request needs the core's token
http://localhost:9840/
the same API through an SSH tunnel, if you'd rather keep the VM private
Deploy with Shelley#
Click Deploy and Shelley will set up Cloudroom on a new VM, then check that it works. No config-file wrangling required.
Prefer a terminal?
curl -fsSL \
https://exe-staging.dev/deploy/cloudroom/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
Set up Cloudroom on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/cloudroom.md from top to bottom.
The recipe's example VM name is my-cloudroom. This VM's name is the output of `hostname`: use it wherever the recipe writes my-cloudroom. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- 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
Read the whole recipe, including the closed details. Run "Install by
hand" only, skipping VM creation and SSH; don't run the restore or
update instructions. Install exactly the pinned version.
Never print, log or send the core's token, and don't make the VM public:
the owner decides that. On an existing VM, ask before replacing anything
on port 8000.
Verify with the checks in each step: /v1/ready through nginx says
ready, capabilities list claude-code and codex, the test task wrote
hello.txt, and the backup timer is scheduled. Do not reboot.
Finish by telling the owner the two ways to connect the app from
"Connect the app", and the command that prints the token.
Finish with the URLs I should open.
Also at /deploy/cloudroom/prompt.txt. The recipe it follows is /deploy/cloudroom.md.
Connect the app#
The app needs an HTTPS URL and the core's token. Get the token on your computer:
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:
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:
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-agentand/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 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.
Install by hand
On your computer (pick another name if my-cloudroom is taken):
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 if you like, then run it with a pinned version:
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.
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:
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#
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 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:
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:
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.
Back up and restore
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:
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:
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:
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.
Update
Cloudroom publishes core releases almost daily. Update the core and the
app together. Pick a core-v version from the
releases, take a
backup, then, replacing X.Y.Z:
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.
Something stuck?
Ask Shelley, or check on the VM:
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/readyshould 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.jsonis missing. Runsudo -iu cloudroom-agent exeuntu configure claudeand 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:
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:
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 withssh exe.dev resize my-cloudroom --disk=100GB.
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 and the agents' model usage through the LLM integration, 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 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 say what each needs.
Updated Oct 31, 2026 · tested end to end Oct 31, 2026 · Markdown · manifest.json · Something wrong? Tell us