---
title: "Self-host QM"
description: "Run QM, Y Combinator's open-source AI teammate, on an exe.dev VM: a private web app, a computer per person, and Claude with no API key."
url: https://exe.dev/deploy/qm
category: "AI agents"
updated: 2026-10-28
---

# Self-host QM

One AI teammate for the whole team, each person with their own computer.

- **Software:** QM (https://github.com/yc-software/qm)
- **Source:** https://github.com/yc-software/qm
- **License:** MIT
- **Time:** 15 minutes
- **Example VM name:** `my-qm` (yours will differ; on the VM, `hostname` prints it)
- **Machine-readable:** https://exe.dev/deploy/qm/manifest.json
- **File:** https://exe.dev/deploy/qm/backup.sh
- **File:** https://exe.dev/deploy/qm/fix-local-sandbox.sh
- **File:** https://exe.dev/deploy/qm/restore.sh

## What you get

- QM at `https://<vm>.exe.xyz/`, behind your exe.dev login and QM's own sign-in, with you as its administrator.
- Claude through exe.dev's LLM integration, with no API key to paste, and GPT models in the admin console.
- Agent computers that run commands and keep their files, each in its own container on the VM.
- A stack that comes back by itself after a reboot.
- Nightly backups of the database, settings, and agent computers in `~/backups/qm`.

Running on the VM:

- **QM portal**: Sign-in and front door for the web app and admin console, port 8000
- **QM core**: The agent runtime, with Pi driving the model, port 7999, data in `qm-team-coredata volume`
- **PostgreSQL 16 with pgvector**: Sessions, memory, settings, and users, data in `qm-team-pgdata volume`
- **Agent computers**: One Docker container per person or room, started on demand, data in `qm-home-* volumes`
- **Claude**: Connected through exe.dev's LLM integration

How it is reached:

- https://my-qm.exe.xyz/ (private): The QM web app and admin console, behind your exe.dev login and a QM sign-in

## Deploy with Shelley

Open https://exe.dev/new?prompt=Set%20up%20QM%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe.dev%2Fdeploy%2Fqm.md%20from%20top%20to%20bottom.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-qm.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-qm.%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-%20QM%20at%20%60https%3A%2F%2F%3Cvm%3E.exe.xyz%2F%60%2C%20behind%20your%20exe.dev%20login%20and%20QM%27s%20own%20sign-in%2C%20with%20you%20as%20its%20administrator.%0A-%20Claude%20through%20exe.dev%27s%20LLM%20integration%2C%20with%20no%20API%20key%20to%20paste%2C%20and%20GPT%20models%20in%20the%20admin%20console.%0A-%20Agent%20computers%20that%20run%20commands%20and%20keep%20their%20files%2C%20each%20in%20its%20own%20container%20on%20the%20VM.%0A-%20A%20stack%20that%20comes%20back%20by%20itself%20after%20a%20reboot.%0A-%20Nightly%20backups%20of%20the%20database%2C%20settings%2C%20and%20agent%20computers%20in%20%60~%2Fbackups%2Fqm%60.%0A%0ARead%20the%20whole%20recipe%2C%20including%20the%20closed%20details.%20For%20initial%20setup%2C%0Afollow%20%22Install%20by%20hand%22%20first%2C%20skipping%20VM%20creation%20and%20SSH.%20The%0Aopening%20walkthrough%20is%20for%20the%20owner.%20Do%20not%20run%20the%20restore%20or%20update%0Ainstructions.%0AKeep%20the%20VM%20private.%20Never%20run%20share%20set-public.%20On%20an%20existing%20VM%2C%20ask%0Abefore%20replacing%20anything%20already%20on%20ports%207999-8002%20or%20a%20%60~%2Fqm%60%20folder.%0ASkip%20%60qm%20doctor%60%3A%20it%20tests%20the%20placeholder%20key%20against%20Anthropic%0Adirectly%20and%20always%20fails%20here.%20Run%20%60fix-local-sandbox.sh%60%20after%20every%0A%60qm%20up%60.%0AVerify%20%60qm%20check%60%20passes%2C%20%60%7B%22ok%22%3Atrue%7D%60%20from%20the%20health%20check%2C%20the%0Apatch%20reports%20itself%20in%20place%2C%20the%20backup%20timer%2C%20and%20a%20backup%20archive.%0ADon%27t%20set%20up%20Slack%20unless%20the%20owner%20asks.%20Do%20not%20reboot.%0AFinish%20by%20running%20%60npm%20exec%20qm%20--%20admin-login%60%20in%20%60~%2Fqm%60%20and%20giving%20the%0Aowner%20the%20link%20it%20prints%2C%20saying%20it%20works%20once%20and%20expires%20in%20five%0Aminutes%3B%20this%20conversation%20is%20private%20to%20the%20owner.%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/qm/prompt.txt | ssh exe.dev new --prompt=/dev/stdin
```

Or follow the steps below by hand.

QM, from Y Combinator, is an open-source AI teammate for a whole company.
Everyone gets their own agent with private memory, files, and a computer
to run commands on, and people can also work with it together in shared
rooms and projects. An admin console sets the models, security rules, and
who can do what.

On exe.dev it runs on one VM that stays on, with every person's computer
as a container beside it. The VM is private: only people you share it
with can reach it at all, and QM signs them in again. Model usage is
billed to your exe.dev account through the
[LLM integration](/docs/integrations-llm), so there is no API key to find.

## Sign in

Shelley's last message holds a one-time admin link. It works once and
expires after five minutes; for a new one, on your computer:

```bash
ssh my-qm.exe.xyz "cd qm && npm exec qm -- admin-login"
```

Open the link, sign in to exe.dev if asked, and click **Sign in**. You land
in the admin console. **Back to home** opens the chat.

> [!WARNING]
> **Keep this VM private.** QM's agents run commands, and the VM holds
> everyone's conversations. Don't run `share set-public`; share it with
> people instead, as below.

## Give it some work

Start a **New session** and try:

> On your computer, check which Python version you have, write a script
> that prints the ten largest files under /usr, and run it.

The first command takes a few seconds while QM starts your computer, a
container of your own. Files you leave in it are still there next time.
Then try something longer-lived:

> Every weekday at 9:00, send me a summary of the top five Hacker News
> stories.

The model menu under the message box switches between Claude models.
**Models** in the admin console sets the default and turns GPT models on.

## Bring in your team

People need two things: access to the VM, and a QM invitation.

On your computer, share the VM with each teammate's email. This gives
them the web app only, not a shell:

```bash
ssh exe.dev share add my-qm teammate@example.com
```

Then in QM's admin console, open **Users**, click **Invite teammate**,
enter the same email, and click **Send invite**. QM can't send email from
this VM, so click **Copy invite link** and send it to them yourself; it
works for 24 hours. Each
person gets their own memory and computer; rooms and projects are where
you work together.

## Keep it safe

Everything lives on the VM: the database, each person's computer, and
the settings and secrets in `~/qm/.env`. A nightly backup puts all of
it in one archive in `~/backups/qm` and keeps the newest 14. Copy them
**off the VM**, too. On your computer:

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

Keep those copies private: they hold every conversation and the keys
that sign people in.

## 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-qm` is taken):

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

All remaining installation commands run **on the VM**, as `exedev`. QM
needs about 12 GB of disk; a default VM has room.

### Install Node.js and start Docker on boot

QM's command-line tool needs Node.js 24.15 or later. Docker must start
on boot, so QM comes back after a restart:

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

Expect `v24.15` or later.

### Create the deployment

QM keeps a deployment in a folder of its own: settings, secrets, and the
skills and tools its agents get. `team` names your organization inside
QM and its containers:

```bash
mkdir -p ~/qm && cd ~/qm
npm exec --yes --package=@yc-software/qm@latest -- \
  qm init . --org team --target docker --model-provider anthropic
npm install
npm exec qm -- --version
```

Expect `0.1.15` or later. `npm install` pins that version in
`package.json`, so the deployment doesn't change under you.

### Point it at exe.dev

Replace the generated settings. The portal takes port 8000, the one the
[HTTPS proxy](/docs/proxy) serves at `https://<vm>.exe.xyz/`, and the
model base URLs send Claude and GPT through the LLM integration:

```bash
cat > qm.config.jsonc <<EOF
{
  "contract": 1,
  "orgId": "team",
  "publicUrl": "https://$(hostname).exe.xyz",
  "basePort": 7999,
  "target": "docker",
  "modelProvider": "anthropic",
  "model": "claude-sonnet-5-5",
  "services": ["core", "web-ui", "admin", "portal", "auth"],
  "plugins": [],
  "skills": [],
  "env": {
    "auth": { "AUTH_EMAIL_TRANSPORT": "smtp" },
    "core": {
      "HARNESS": "pi",
      "ANTHROPIC_BASE_URL": "https://llm.int.exe.xyz/anthropic",
      "OPENAI_BASE_URL": "https://llm.int.exe.xyz/openai/v1"
    }
  },
  "secretEnv": { "core": { "ADMIN_GRANTS": "ADMIN_GRANTS" } },
  "sandbox": { "backend": "local" }
}
EOF
```

`basePort` puts core on 7999, the portal on 8000, the web app on 8001,
and the admin console on 8002. Those ports are private too. The
`local` sandbox gives each person a Docker container on this VM as their
computer. The email setting is required, but no mail server is set, so
QM sends no email; you sign in with one-time links instead.

### Add the secrets

`qm init` generated most secrets in `~/qm/.env`, which only you can read.
Add the rest: you as administrator (your exe.dev email, from the
[reflection integration](/docs/integrations-reflection)), placeholder
model keys (exe.dev supplies the real credentials outside the VM),
core's address for the agent computers, and the sign-in keys:

```bash
setenv() { (umask 077; grep -v "^$1=" .env > .env.new || true; printf '%s=%s\n' "$1" "$2" >> .env.new; mv .env.new .env); }
unset_or_empty() { ! grep -q "^$1=." .env; }
ADMIN_EMAIL=$(curl -fsS https://reflection.int.exe.xyz/email | jq -r .email | tr 'A-Z' 'a-z')
setenv ADMIN_GRANTS "$ADMIN_EMAIL:org_admin"
setenv AUTH_ALLOWED_EMAILS "$ADMIN_EMAIL"
setenv ANTHROPIC_API_KEY exe-llm-integration
setenv OPENAI_API_KEY exe-llm-integration
setenv PUBLIC_API_URL http://qm-team-core:8080
for k in AUTH_CLIENT_SECRET AUTH_TOKEN_SECRET PORTAL_SESSION_SECRET; do
  unset_or_empty $k && setenv $k "$(openssl rand -hex 32)"
done
unset_or_empty AUTH_SIGNING_JWK && setenv AUTH_SIGNING_JWK "$(node -e "const {generateKeyPairSync}=require('node:crypto');process.stdout.write(JSON.stringify(generateKeyPairSync('ec',{namedCurve:'P-256'}).privateKey.export({format:'jwk'})))")"
echo "admin: $ADMIN_EMAIL"
npm exec qm -- check
```

Expect your email and a passing check. It notes that sign-in email is
off and points to `qm admin-login`; that's expected. Teammates you invite
later get in by invitation, whatever this allowlist says.

### Start QM

The first start downloads about 4 GB of images and builds the agent
computer image, which takes a few minutes:

```bash
npm exec qm -- up
curl -fsS http://127.0.0.1:8000/healthz; echo
```

Expect `✓ stack up — team` and `{"ok":true}`. The containers restart on
their own after a reboot.

### Let core reach the agent computers

QM 0.1.15 doesn't tell core which container it runs in, so core can't
reach the agent computers, and every command fails with `exec daemon
never became reachable`. The fix is
[upstream](https://github.com/yc-software/qm/pull/2712) but not released.
[fix-local-sandbox.sh](https://exe.dev/deploy/qm/fix-local-sandbox.sh)
copies that one-line change into the running core container and
restarts it. Run it after every `qm up`; once a release has the fix, it
says so and does nothing:

```bash
curl -fsSL -o ~/qm/fix-local-sandbox.sh https://exe.dev/deploy/qm/fix-local-sandbox.sh
chmod +x ~/qm/fix-local-sandbox.sh
~/qm/fix-local-sandbox.sh
~/qm/fix-local-sandbox.sh
```

Expect `qm-team-core patched and restarted` (or, on a fixed release,
`already passes its container name`), then `already passes its
container name` the second time.

### Set up nightly backups

[backup.sh](https://exe.dev/deploy/qm/backup.sh) writes one private
archive: the `~/qm` folder, the QM tool's state in `~/.config/qm`, a
database dump, and every agent computer's files.
[restore.sh](https://exe.dev/deploy/qm/restore.sh) puts one back.
Lingering keeps the timer running when you're logged out:

```bash
curl -fsSL -o ~/qm/backup.sh https://exe.dev/deploy/qm/backup.sh
curl -fsSL -o ~/qm/restore.sh https://exe.dev/deploy/qm/restore.sh
chmod +x ~/qm/backup.sh ~/qm/restore.sh
sudo loginctl enable-linger "$USER"
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/qm-backup.service <<'EOF'
[Unit]
Description=QM backup

[Service]
Type=oneshot
ExecStart=%h/qm/backup.sh team %h/qm %h/backups/qm
EOF
cat > ~/.config/systemd/user/qm-backup.timer <<'EOF'
[Unit]
Description=Nightly QM backup

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

[Install]
WantedBy=timers.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now qm-backup.timer
systemctl --user start qm-backup.service
tar -tf "$(ls -t ~/backups/qm/*.tar | head -1)"
systemctl --user list-timers qm-backup.timer
```

Expect `./qm.dump`, `./deployment.tgz`, and `./volumes/qm-team-coredata.tgz`
in the archive, and a scheduled next run.

### Sign in

Print a one-time admin link, good for five minutes:

```bash
cd ~/qm && npm exec qm -- admin-login
```

Open it, click **Sign in**, and go [give it some work](#give-it-some-work).

</details>

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

On the VM, take a fresh backup:

```bash
~/qm/backup.sh
```

To restore on the same VM, pick an archive. QM stops for a few seconds,
and everything since that backup is replaced:

```bash
~/qm/restore.sh ~/backups/qm/qm-team-YYYYMMDD-HHMMSS.tar
curl -fsS http://127.0.0.1:8000/healthz; echo
```

Expect `restored …` and `{"ok":true}`. Agent computers start again from
their restored files the next time someone uses them.

Moving to a new VM? The archive's settings must be in place before QM
first starts there, because they hold the database password. On the new
VM, [install Node.js and Docker](#install-nodejs-and-start-docker-on-boot),
copy the archive over with `scp`, then:

```bash
mkdir -p ~/restore && tar -C ~/restore -xf qm-team-YYYYMMDD-HHMMSS.tar
tar -C ~ -xzf ~/restore/deployment.tgz
cd ~/qm && sed -i "s|https://[a-z0-9-]*\.exe\.xyz|https://$(hostname).exe.xyz|" qm.config.jsonc
npm install && npm exec qm -- up && ./fix-local-sandbox.sh
./restore.sh ~/qm-team-YYYYMMDD-HHMMSS.tar
```

Then set up the [nightly backups](#set-up-nightly-backups) again.

</details>

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

On the VM, back up, move to the newest QM, restart with it, and patch
core again:

```bash
cd ~/qm &&
./backup.sh &&
npm install @yc-software/qm@latest &&
npm exec qm -- --version &&
npm exec qm -- check &&
npm exec qm -- up &&
./fix-local-sandbox.sh
```

Expect the new version, `✓ stack up — team`, and a line from the fix.
Check QM's [releases](https://github.com/yc-software/qm/releases) first:
there is no automatic rollback, so the backup is how you go back.

</details>

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

Ask Shelley, or check on the VM:

```bash
cd ~/qm
npm exec qm -- status
npm exec qm -- logs core
docker ps --format '{{.Names}} {{.Status}}'
```

- **`exec daemon never became reachable`**, or the agent says its
  computer is broken? Run `~/qm/fix-local-sandbox.sh`; any `qm up`
  undoes it until QM ships the fix. Then ask the agent to try again.
- **`qm doctor` says Anthropic rejected `ANTHROPIC_API_KEY`?** It tests
  the placeholder against Anthropic directly. Ignore it if chats get
  replies.
- **401 or a QM sign-in page instead of the chat?** Get a fresh link with
  `npm exec qm -- admin-login`; each one works once, for five minutes.
- **A teammate can't get in?** They need both the exe.dev share and a QM
  invitation, under the same email.
- **No model replies?** Check `ANTHROPIC_BASE_URL` in `qm.config.jsonc`
  against the [settings above](#point-it-at-exedev), and your
  [LLM integration](/docs/integrations-llm).
- **Nothing after a reboot?** `sudo systemctl enable --now docker.service`;
  the containers start with Docker.
- **Out of disk?** `df -h /`; QM's images take about 8 GB. Grow the
  disk from your computer with `ssh exe.dev resize my-qm --disk=50GB`.

</details>

## Frequently asked questions

### What is QM?

An open-source agent from Y Combinator, built for a whole company rather than one person. Each person, and each shared room or project, gets its own memory, files, permissions, scheduled jobs, and computer. People reach it from a web app or Slack, and an admin console sets the rules.

### What does it cost?

Your [VM plan](https://exe.dev/pricing) plus model usage, billed through the [LLM integration](/docs/integrations-llm). QM's admin console has a **Spend** page per person and session.

### Can I use GPT models or my own key?

GPT models are already wired to the LLM integration; pick them in the admin console's **Models** page. To bring your own key, change the [LLM integration](/docs/integrations-llm), or put a real `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` in `~/qm/.env`, remove the matching base URL from `qm.config.jsonc`, and run `qm up` again.

### How many people can use one VM?

Each person's computer is a container that starts when they need it, so memory is the limit. A few people fit on a default VM; for a bigger team, give it more with [`resize`](/api?cmd=help+resize), for example `ssh exe.dev resize my-qm --memory=16GB`.

### Can it work in Slack?

Yes. QM connects to Slack outward, so the VM can stay private. Ask Shelley to follow "Add the Slack bot" in `~/qm/deployment.md`, the guide QM wrote for your deployment; it takes a Slack app you create from the manifest it renders.

### Does it keep working when my laptop is closed?

Yes. QM runs on the VM, so scheduled jobs and webhooks carry on, and the whole stack comes back after a reboot.


## Related

- [Self-host Hermes Agent](https://exe.dev/deploy/hermes-agent.md): An agent that remembers you, on a computer that never sleeps.
- [Host OpenClaw](https://exe.dev/deploy/openclaw.md): An AI sidekick with its own computer. Your laptop can take a nap.
- [Run Claude Code on a remote server](https://exe.dev/deploy/claude-code.md): A computer for Claude. A breather for your laptop.
- [Running Agents](https://exe.dev/deploy/agents.md): Use exe.dev VMs as a sandbox
- [Run Codex on a remote server](https://exe.dev/deploy/codex.md): A computer for Codex. An early night for your laptop.
- [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.
