Run nanoMuse yourself
nanoMuse is one agent on your own devices, and nothing in it has to go through a server of ours. There are three ways to run it yourself, from the least work to the most. Most people want the first; a family or a small group that wants its own sign-in wants the second; the third is for the web app on a machine of your own.
| What you run | What it gives you | |
|---|---|---|
| 1. No server, your own key | nothing | the apps talk to a model provider directly; the community relay is still used for sign-in and for the devices to find each other |
| 2. Your own relay | nanoMuse Cloud on a small VPS or a home server | your own accounts, sign-in codes, hub and conversation sync; nothing about your account touches nanomuse.cn |
| 3. A runtime of your own | the Python runtime in Docker | the web app and the hands on a computer that is always on |
1. No server, your own key
The free allowance on the community relay runs out. When it does, or before, put a key of your own in the app — Alibaba Model Studio (百炼) inside mainland China, OpenRouter or any OpenAI-compatible endpoint elsewhere — and the model requests go from your device to that provider; the relay only sees the sign-in and the hub frames between your devices. own-key.md has the steps for every app. Nothing to install, nothing to keep running.
2. Your own relay
nanoMuse Cloud is the relay behind nanomuse.cn: sign-in by e-mail or phone, the account's models through one OpenAI-compatible endpoint, the hub the devices meet on, conversation sync, a web console at /app and an admin page at /app/admin/. It is one Python service and one SQLite file, in this repository under cloud/, and it runs the same on a server of yours.
What you need
- A machine. A VPS with 1 vCPU and 1 GB of memory is enough for a family; the relay idles at a few tens of megabytes and the model work happens at the provider. Such a server costs on the order of ¥30–60 a month inside mainland China or US$4–6 outside it, from any provider — the relay does not care which. A home server or a spare laptop works for a LAN-only relay.
- A domain pointing at the machine (an A record), with ports 80 and 443 reachable. Caddy, which runs next to the relay, fetches and renews the Let's Encrypt certificate on its own. Skip this for a LAN-only relay.
- A model provider key. The relay spends your money on your provider: everyone who signs in on your relay is billed to that key, at the provider's prices, within the allowance you set (
ALLOWANCE_CNY,SIGNUP_OPEN, members inALLOWED_IDENTIFIERS— cloud/README.md). Without a key, sign-in and the hub work and chat does not. - Docker with the compose plugin, and
curl.
In one command
git clone https://github.com/nano-muse/nanoMuse.git && cd nanoMuse
bash scripts/self-host.shThe script asks five things — the domain (or local), an e-mail for Let's Encrypt, how sign-in codes go out, the provider's URL and key, the admin password — writes cloud/.env with a fresh random CLOUD_SECRET, starts docker compose, waits for /healthz and prints the console and admin URLs. Running it again keeps the secrets and the accounts and only re-asks, with the current values as defaults. For this machine only, no domain and no TLS:
bash scripts/self-host.sh --local # http://127.0.0.1:8787, codes in the relay's log
bash scripts/self-host.sh --local --bind 0.0.0.0 # reachable from the phones on the same networkBy hand instead of the script: cd cloud && cp .env.example .env, fill in CLOUD_DOMAIN, PUBLIC_BASE, ACME_EMAIL, CLOUD_SECRET, CLOUD_ADMIN_TOKEN, UPSTREAM_KEY and the sender settings, then docker compose up -d — or, without Caddy, docker compose -f docker-compose.yml -f docker-compose.local.yml up -d.
Sign-in codes
A code goes out every time someone signs in. CODE_SENDER says how:
log— printed to the relay's log only:cd cloud && docker compose logs relayshowsverification code for …. Fine for a family: you read the code out. This is what--localuses.smtp— by e-mail through a mailbox you own (SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASSWORD,SMTP_FROM). Any mailbox with SMTP works; a free one is enough for a few people.aliyun— by SMS to mainland-China numbers through Aliyun Dysmsapi; needs a signed template.bothuses SMTP for addresses and Aliyun for numbers. SMS reaches mainland numbers only; everyone else signs in by e-mail.
The admin page
https://<your domain>/app/admin/ with the admin password (CLOUD_ADMIN_TOKEN in cloud/.env): the accounts, what each has spent, allowance and membership, nudge policies, the devices on the hub, the relay's own health. The same data is behind /v1/admin/* for scripts (cloud/README.md).
Pointing the apps at it
Every app signs in against https://cloud.nanomuse.cn unless you tell it otherwise. The devices of one account must all point at the same relay — the hub and the conversations live there.
Android (0.1.38). Use a different server under the sign-in form takes the relay's address; Check asks its
/healthzand shows the version, Use this server keeps the address across launches.https://is required outside your own network; plainhttp://is accepted for a private address (10.x,172.16–31.x,192.168.x,localhost, a.localor.ts.netname). Settings → Account shows the server with Change, which signs the phone out first (android.md).iPhone / iPad. The same link on the sign-in sheet, with the same rules.
nanoMuse Desktop. The relay is the
baseURLof the plugin's cloud row. Add this tocordis.patch.ymlin the desktop's profile —~/.nanomuse/desktop/profiles/nanomuse/(%USERPROFILE%\.nanomuse\desktop\profiles\nanomuse\on Windows;NANOMUSE_DESKTOP_HOMEmoves it) — and restart the app:yaml- id: nanomuse-cloud name: dsh-nanomuse/cloud config: baseURL: https://cloud.example.com deviceName: '' statePath: ''Setting
NANOMUSE_CLOUD_URL=https://cloud.example.comin the environment the app starts in does the same without editing the file (desktop.md).The terminal runtime and the hosted web app (
nanomuse …, the rootdocker-compose.yml):NANOMUSE_CLOUD_BASE_URL=https://cloud.example.comin the environment or.env, or[cloud] base_urlinconfig/config.toml(configuration.md).The web console is the relay's own
/app—https://<your domain>/app/— and needs no pointing.
Invitations from your relay link to its console (INVITE_URL, set by the script), so a friend you invite lands on your relay, not on nanomuse.cn.
Updating
cd nanoMuse && git pull
bash scripts/self-host.sh # Enter through the questions; secrets and accounts are kept
# or: cd cloud && docker compose up -d --buildThe database schema migrates forward on start. Read the release notes before a jump of several versions; the relay's version is in GET /healthz.
Backing up
Everything the relay keeps is cloud/data/cloud.db (SQLite) plus CLOUD_SECRET in cloud/.env. The database is useless without the secret — the identifiers are hashed and encrypted with it — and the secret is useless without the database; back them up together, and never rotate the secret unless you mean to start over:
cd cloud && docker compose exec relay sqlite3 /srv/nanomuse-cloud/data/cloud.db ".backup /srv/nanomuse-cloud/data/backup.db" \
|| cp data/cloud.db data/backup.db # when the container has no sqlite3: stop the relay first for a clean copyCaddy's certificates live in the caddy_data volume and are fetched again if lost.
What your relay stores
Hashed and encrypted identifiers, per-request counts and prices, the devices on the hub, and — only while Sync conversations between my devices is on — the text of the conversations so the other devices can show them. No files, no screenshots, no passwords. The full list, and what each switch does, is privacy.md; as the operator you are the one it now describes.
3. A runtime of your own, for the web app
The phone and the desktop carry their own agent. The web app does not — it talks to the Python runtime, which on nanomuse.cn runs in a container per visitor. You can run that runtime on a machine of your own, in Docker, and open the web app on it from any browser:
cp .env.example .env && $EDITOR .env # a model key, NANOMUSE_SERVER_TOKEN; NANOMUSE_CLOUD_BASE_URL for your own relay
nanomuse config init # or copy config/config.example.toml to config/config.toml
docker compose up -d app # the web app on http://<host>:8787, the URL with its token in the logsThe container sees only its data volume and ./workspace; capabilities are dropped. Keeping it running, a published image for arm64, and reaching it from outside your network are in deployment.md. The runtime signs in to a relay like any other device — yours, from section 2, if you set NANOMUSE_CLOUD_BASE_URL — and then the phone can hand it tasks over the hub like a computer that is always on (every-device.md).