Skip to content

Deploy to a Fresh Server From Scratch ​

Who is this page for?

You just got hold of a brand-new Ubuntu / Debian server (cloud VM, local VM, internal box — all fine) and want to bring up anet hub + a node or two + Telegram from zero, end-to-end.

Every step has a verify command, and the end of the page has a common-error lookup (troubleshooting table).

It is not for users who already have anet installed; that's Getting Started or Upgrade Guide. To move an existing Hub to another machine, follow Moving the Hub; a fresh deployment does not restore its data.

0. Prerequisites ​

Install Node.js ≥ 22.13 and Bun ≥ 1.2 as described in Install (you need both; the Hub runs on Bun). If systemd, cron or another user will start the Hub and nodes later, read the PATH note on that page first.

1. Install the anet CLI ​

bash
npm install -g @sleep2agi/agent-network
anet -v

anet -v shows whether agent-node and commhub-server are in place (they are fetched on first use) and which optional runtime CLIs are installed. Whenever something fails to start later, look here first.

CommHub is a long-running process: closing the terminal stops it. For production you'd configure systemd (§8 Persistence); for a quick verify, use tmux:

bash
# Install tmux (if not present)
sudo apt install tmux -y    # Ubuntu / Debian

# Start a tmux session named anet-hub
tmux new -s anet-hub
anet hub start --host 0.0.0.0 --port 9200

# Look for output like this — that's success:
# CommHub MCP Server <version>
# REST:   http://0.0.0.0:9200/api
# ✅ Admin account created
# username: admin
# password: anet-<22 random hex chars>   ← printed only this once; save it

Press Ctrl+B then D to detach the tmux session while keeping it alive. Come back with tmux a -t anet-hub.

Verify the hub is up (from another terminal):

bash
curl -s http://127.0.0.1:9200/health | head -5
# Expect something like {"ok":true,"version":"<version>",...}

Public-internet deploy: change the password immediately

The random password from the first start is shown only once. Any --host 0.0.0.0 public-internet deploy must immediately change it to your own strong password:

bash
anet login --username admin --password '<password printed at startup>' --hub http://127.0.0.1:9200
anet passwd          # Interactive password change (≥ 8 chars, not in the weak-password dict)

Or set credentials when starting the hub: anet hub start --host 0.0.0.0 --username <your-admin> --password '<strong-pass>'.

Port already in use? Change ports or stop the old hub
bash
# Who's listening on the port
ss -tlnp | grep 9200

# Change to a different port
anet hub start --port 9201

# Or gracefully stop the old hub (preferred)
anet hub stop                  # default port 9200, SIGTERM → 3s grace → SIGKILL fallback
anet hub stop --port 9201      # non-default port
anet hub status                # show current PID / port / commhub-server version

# Fallback if the above somehow doesn't work
HUB_PID=$(ss -tlnp | grep ':9200' | sed -E 's/.*pid=([0-9]+).*/\1/' | head -1)
kill "$HUB_PID"
# Or if your hub runs under tmux, attach and Ctrl+C
tmux a -t anet-hub             # Then Ctrl+C to stop, Ctrl+B D to keep the tmux session

On older versions (such as 2.2.12) anet hub --help does not list stop / status, but the commands work: anet hub status returns hub running / vX.Y.Z / pid N.

3. CLI Login ​

Open a third terminal (leave the hub's tmux alone):

bash
anet login --hub http://127.0.0.1:9200 --username admin --password '<password printed at startup>'

After login, the token lives at ~/.anet/config.json and is auto-attached to every subsequent anet node ... command.

bash
anet whoami          # Confirm your identity

4. Create an Agent Node (chosen by runtime) ​

bash
anet node create my-bot

The wizard asks the following, in order:

text
node-name → runtime → (only if claude-agent-sdk) vendor → model → API key / auth

Runtime is the first fork: it decides whether you'll be asked for a vendor and what dependencies you'll need. The picker lists all 7 runtimes; the table below covers the common ones. For npm packages, default models and the full wizard behavior, see runtimes — canonical table:

RuntimeComplexityBest forWizard follow-upExtra dependencies
claude-code-cli (recommended for first-time users)⭐You already use Claude Code, want to reuse the subscriptionSkips vendor + model, uses your local claude loginclaude auth login already done
claude-agent-sdk⭐⭐Programmatic access to any Anthropic-compatible API (MiniMax / 书生 / 小米 MiMo / domestic models go here)Pops a vendor submenu → pick vendor → pick model → enter API keyAPI key
codex-sdk⭐⭐⭐Writing code / running commands via OpenAI CodexSkips vendor, uses codex's own authagent-node + codex CLI + codex login
grok-build-acp⭐⭐⭐Running tasks via xAI Grok BuildSkips vendor, uses grok's own auth + GROK_CODE_XAI_API_KEYgrok CLI + grok login + GROK_CODE_XAI_API_KEY
opencode-cli (preview)⭐⭐⭐Use the public sst/opencode CLI as a multi-vendor front-end (preview; in the menu on both latest and preview)Pick a vendor preset (anthropic / openai), key read from envopencode CLI + Anthropic/OpenAI env key

Watch out for the default runtime

The wizard defaults to the first option (currently claude-agent-sdk); a new user pressing Enter all the way lands on the vendor + API-key path. Manually pick claude-code-cli for the smoothest first-time experience.

The wizard does not ask about Telegram

Once the steps above finish, the wizard ends; it never asks for a Telegram bot token / allow-list UID. Telegram is attached after node creation with a separate command, anet channel add telegram (see §6).

If you see the line "optional Telegram channel" at the top of anet create's output, don't assume the wizard will configure it for you; it won't.

After completion, the node config is written under cwd:

text
.anet/nodes/my-bot/config.json

Verify:

bash
anet node ls         # should list my-bot

5. (codex-sdk only) Install agent-node + codex CLI ​

If you picked codex-sdk or claude-agent-sdk above, the node needs the @sleep2agi/agent-node package to run. It is lazily fetched via npx on first use; if that fetch doesn't happen, the node fails on start with agent-node is not installed or cannot report a version. Run: anet upgrade.

The reliable fallback: install it once manually:

bash
npm i -g @sleep2agi/agent-node

# Verify
agent-node --version    # should print a version

The codex-sdk runtime additionally needs the codex CLI installed + logged in:

bash
# Note: this is @openai/codex (the CLI tool), distinct from @openai/codex-sdk (the SDK library that the codex-sdk runtime depends on) — two separate npm packages
npm i -g @openai/codex
codex login        # Browser OAuth

# Verify
codex --version

The claude-agent-sdk runtime does not need either codex / claude CLI: as long as your node config carries the right vendor's API key (or the matching env vars ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN), you're good.

6. Configure the Telegram channel (optional) ​

If you want to dispatch tasks to the agent from Telegram:

6.1 Get a bot token + your Telegram user ID ​

  1. In Telegram, search @BotFather → /newbot → follow the prompts; the last reply contains your bot-token (looks like 1234567:ABCDef...)
  2. Get your Telegram user ID (a number): message @userinfobot anything; it replies with your UID

6.2 Attach the Telegram channel to the node ​

bash
anet channel add telegram my-bot \
  --bot-token <bot-token> \
  --allow <uid>

Parameters:

  • <bot-token>: the string BotFather gave you
  • <uid>: the Telegram user ID(s) allowed to dispatch tasks to this bot (comma-separate multiples: --allow 11111,22222)

Verify it landed:

bash
anet channel ls
# Should list: my-bot (my-bot) telegram     allow: <uid>

Want to add more allowed users?

anet channel add telegram <node> --allow <new-uid-list> overwrites the allowlist, it doesn't append. To keep existing entries, repeat the full list (--allow 11111,22222,33333). More known limits: guide/channels.md.

7. Start the Node ​

bash
# Recommended: tmux (same as the hub — avoids stopping when the terminal closes)
tmux new -s anet-my-bot
anet node start my-bot

claude-code-cli first launch pops the "dev-channels" confirmation prompt

On its first launch, a claude-code-cli node will pop Claude Code's --dangerously-load-development-channels confirmation prompt:

text
❯ 1. I am using this for local development
  2. Exit

Press 1 + Enter. In scripted / detached launches nobody's there to answer, so the node hangs and stays offline.

Workaround: run it once in a foreground tmux and answer the prompt manually; subsequent launches don't pop it (the answer is session-scoped). You can then layer systemd on top.

When you see SSE connected, the node is online; back on the hub side, anet status should list my-bot as online.

Ctrl+B D to detach the tmux session.

8. Persistence (systemd / tmux) ​

Two options below: tmux for quick / dev use, systemd units for production / autostart.

8.1 tmux (dev / quick verify) ​

A machine reboot drops everything; you restart by hand:

  • hub: tmux new -s anet-hub + anet hub start --host 0.0.0.0
  • each node: tmux new -s anet-<alias> + anet node start <alias>
  • An @reboot crontab line can replace the manual step, but you'll need to solve the non-interactive-shell PATH / nvm problem first (see §0)

Confirm the node really came up; don't trust the stdout ✅

After starting each node, run tmux has-session -t "=<alias>"; echo $? and expect 0. The = is required: a bare name is a prefix match and can go green on another session. Versions before 2.3.0-preview.40 can print ✅ started detached (tmux session live) and exit 0 on the detached path while nothing runs in tmux. For bulk launches use anet project up; its exit code is trustworthy on ≥ 2.3.0-preview.40.

8.2 systemd units (production / autostart) ​

The unit files below are not officially tested. Swap in your real anet path (from which anet) and the user you run anet under, then systemctl daemon-reload + enable --now.

Hub /etc/systemd/system/anet-hub.service:

ini
[Unit]
Description=Agent Network Hub
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser
# Replace with the absolute path from `which anet` ↓
ExecStart=/home/youruser/.nvm/versions/node/v22.13.0/bin/anet hub start --host 0.0.0.0 --port 9200
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Node (templated — one unit runs N nodes) /etc/systemd/system/anet-node@.service:

ini
[Unit]
Description=Agent Network Node %i
After=anet-hub.service
Requires=anet-hub.service

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser
ExecStart=/home/youruser/.nvm/versions/node/v22.13.0/bin/anet node start %i
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Enable:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now anet-hub
sudo systemctl enable --now anet-node@my-bot
sudo systemctl status anet-hub anet-node@my-bot

Gotchas:

  • ExecStart must be the absolute path to anet (which anet); systemd does not read nvm's ~/.bashrc
  • User= should be the everyday user that owns the anet install; don't use root
  • The claude-code-cli runtime pops a dev-channels confirmation on first run (see §7 Start the Node); answer it once under a foreground tmux before handing off to systemd
  • Want an official unit / improvements to the template above? PRs welcome, or open a thread on GitHub Discussions

Running in containers ​

There is no maintained production compose bundle yet; the Dockerfiles and compose files in the repository are for tests or specific integrations. In a container, treat it as "Ubuntu with Node.js and Bun": every command above works the same. tests/Dockerfile (the minimal Node.js + Bun + anet set) is a starting point; for production add multi-stage builds, pinned image hashes and a non-root user yourself.

  • Same start order: Hub → Dashboard → anet login → anet node create → anet project up; containerizing only splits them into services.
  • Slow first start: the first anet hub start fetches a pinned commhub-server from npm and caches it in $HOME/.bun/install/cache; pre-warming that directory speeds things up a lot.
  • Container networking: the Dashboard calls the Hub on :9200 over REST + SSE, so both must share a docker network, or publish the Hub port to the host. Inside a container, localhost means that container.
  • Persistence: node state lives in .anet/nodes/<alias>/ under the working directory and Hub data in ~/.commhub/; mount both as volumes.
  • Do not run the node process as PID 1: wrap it in an init such as tini, otherwise SIGTERM skips the node's offline notification.

Troubleshooting table ​

In the order of the setup steps — symptom → cause → fix:

#SymptomCauseFix
1anet hub start → spawn bunx ENOENT + node-stack crashBun not installed; commhub-server is Bun-shebang TSnpm i -g bun or curl -fsSL https://bun.sh/install | bash; afterwards bun --version should produce output
2anet node create → after picking runtime → FATAL: TypeError: fetch failedNode creation needs to register with the local hub, but the hub isn't up (usually because of row 1)Open another terminal, anet hub start, then retry
3Pressing Enter through the wizard lands you on the vendor + API-key pathRuntime menu defaults to claude-agent-sdk, not the simpler claude-code-cliManually pick claude-code-cli when creating the node (with claude auth login already done, it reuses the subscription directly). If a Ctrl-C during vendor selection left a half-baked node behind, clean up with anet node delete <alias> and retry
4Telegram doesn't work after node creation, even though the wizard mentioned "optional Telegram channel"The wizard never asks about Telegram; that line is misleading copyUse anet channel add telegram <node> --bot-token <tok> --allow <uid> separately to attach it (see §6)
5anet node start (codex-sdk / claude-agent-sdk) → agent-node is not installed or cannot report a versionnpx lazy-load didn't actually pull @sleep2agi/agent-node; builds ≤ 2.3.0-preview.37 (including 2.2.x) have this problemShort-term: npm i -g @sleep2agi/agent-node to put the binary in place; long-term: anet upgrade to ≥ 2.3.0-preview.38 (check what each channel points to with npm view @sleep2agi/agent-network dist-tags)
5.5anet node start prints ✅ started detached (tmux session live) and exit 0, but tmux ls has no such session and the process is goneFalse success on the detached path; versions before 2.3.0-preview.40 have this problemReal check: tmux has-session -t "=<alias>"; echo $? should print 0 (the = is required). For bulk launches use anet project up (its exit code is trustworthy on ≥ 2.3.0-preview.40). Until you upgrade, confirm every start with has-session
6claude-code-cli node starts but stays offline / pane is stuck on the confirmation promptClaude Code's --dangerously-load-development-channels prompt is waiting for an EnterRun it once in foreground tmux and press 1 + Enter; subsequent launches don't pop it
7systemd / cron / a different user launch produces a string of command not found errorsnvm + Bun each install per user; non-interactive shells don't load themSymlink node/npm/bun into /usr/local/bin/, or have the launch script explicitly source ~/.nvm/nvm.sh
8Machine reboot drops everythingHub + nodes all rely on manual tmux sessionsConfigure autostart via systemd; see the template in §8.2 systemd units

Next ​

If you hit a problem not covered here, please open a GitHub issue with the step that failed, the exact error, and the anet -v output.

Powered by Sleep2AGI

Download the app