Skip to main content
Condor uses a two-server architecture: the Condor Server (agentic layer) and the Hummingbot API Server (execution layer). This guide installs both.
Secure your API before going live. Hummingbot API can place orders, read balances, and manage bots. AI assistants (MCP, Condor agents, and similar tools) make that surface easier to reach—and easier to misuse if the API is open on the public internet.We recommend Tailscale for production, especially when Condor and the API run on different machines. Tailscale puts the API on a private encrypted network so only your devices can connect—without publishing port 8000 on a public IP. Use strong API passwords too; Tailscale is network isolation on top of auth, not a replacement for it.Full walkthrough: Securing Condor and Hummingbot API with Tailscale · Hummingbot API Tailscale guide

Prerequisites

Any cloud provider works. Popular choices:
  • AWS EC2: t3.medium or larger
  • Google Cloud: e2-medium or larger
  • Digital Ocean: Basic Droplet with 4GB RAM
  • Hetzner: CX21 or larger
Allow SSH (port 22) for administration. Do not expose port 8000 publicly for production—use Tailscale so Condor and other clients reach the API at http://hummingbot-api:8000 on your private tailnet.

Do I Need Tailscale?

What binds where by default

Every port in the Hummingbot API stack is on loopback out of the box. The one port that is not is Condor’s own dashboard, and only in Telegram mode — which is the mode that authenticates. EMQX’s other listeners — 8883, 8083, 8084, 8081, 61613 — are not published at all, since nothing in this stack speaks them. The Condor dashboard is the one thing that binds all interfaces by default, and only in Telegram mode, which authenticates. Local mode has no login, so it binds loopback and stays there unless you set WEB_HOST yourself; turning Tailscale on binds loopback too, because tailscale serve is then what exposes it — to your tailnet only. None of this is a substitute for the credentials. A loopback bind protects the port; it does nothing about anyone already on that machine.
Installed before the API’s port lockdown? Check it. Hummingbot API used to publish port 8000 on every interface, including a VPS’s public IP. It now binds 127.0.0.1 by default (${API_BIND:-127.0.0.1} in docker-compose.yml), and so do Postgres and the EMQX broker — but an existing stack keeps its old bindings until the containers are recreated. Run make deploy in hummingbot-api, then make doctor, which flags any of those ports still sitting on a public interface.If you do need the API reachable off-box, widen it deliberately with API_BIND in hummingbot-api/.env — prefer a specific interface over 0.0.0.0, and with the Tailscale overlay API_BIND=<your-tailscale-ip> keeps MagicDNS working without publishing anything to the internet. Note that Docker’s published ports are not blocked by ufw: its rules are evaluated before ufw’s, so ufw deny 8000 leaves a widened port reachable unless you write DOCKER-USER rules yourself. Close the port in your cloud provider’s firewall instead — that is enforced off-host.
If you’re just testing locally, skip ahead—the Quick Install below works without Tailscale. See Learning Path for more use-case-based routing.

Quick Install

From an empty directory:
Interactive setup will prompt for, in order:
  1. How you will use Condor — Telegram or Local. See Choose your install below.
  2. Telegram Bot Token and Telegram User ID (Telegram mode only): create the bot via @BotFather, get your id via @userinfobot.
  3. Tailscale, yes or no — whether to secure the connection to Hummingbot API (and the dashboard) with Tailscale. Answer y for production or any remote setup. You paste the tskey-auth-... key later, at the point it is actually needed.
  4. Public IP or hostname (Telegram mode without Tailscale only) — press Enter if Condor runs on your own machine, or give the server’s public address so the /web dashboard link works from your phone.
  5. AI model — the wizard offers to pick the model your Trading Agents think with, and installs its CLI bridge for you. It asks before it downloads anything and tells you the cost (~250MB on a first run, 1–3 minutes), so declining is instant. You can skip it and run make pick-model later. See Integrating your LLM.
  6. Whether to configure and launch Hummingbot API with Docker — answer yes if you are deploying Condor and the API on the same machine. Have Docker installed and running first. If the API runs on another server, skip this and enter that API’s URL instead. If an API is already running on port 8000, the wizard offers to keep it and asks for its credentials rather than replacing it.
  7. Tailscale auth key (if you said yes at step 3) — paste your tskey-auth-... key from the Tailscale admin console. When Condor is deploying Hummingbot API on this same machine, it also asks whether that API should get its own tailnet node. Answer no unless another device needs to reach the API directly — Condor itself reaches it over localhost either way.
  8. Hummingbot API credentials — an admin username, password, and a config password. These are required and have no defaults: whatever you enter is written to the API’s .env and to Condor’s config.yml so the two sides match. Passwords are not echoed as you type.
make install finishes by running make doctor for you, so the first report arrives without asking. It cannot fail the install—a warning there never blocks setup—so read it rather than assuming a clean exit meant a clean report.Run it again any time. It re-checks everything the wizard only confirms once: dependencies (uv, tmux, node, npm, tsc), .env/config.yml sanity, the AI model, the dashboard’s network binding, Tailscale, and whether each Hummingbot API server in config.yml is reachable and authenticating. It is read-only and safe to run at any time, and exits non-zero only on real failures—warnings do not.

Choose your install

Three paths. All three run the same agents, bots, routines and web dashboard — what differs is who can reach them and where alerts go.

Local

Your own machine, no Telegram account. Dashboard only, no login.

Local + Telegram alerts

Your own machine, plus a bot that pushes alerts and approvals to your phone.

Server

A VPS running 24/7. Reachable from anywhere over Tailscale.

Local

No bot, no token, no Telegram account. The dashboard runs on the machine you installed on and you are already logged in when you open it:
Local mode has no login. Anyone who can reach the port has full trading control.
  • It binds 127.0.0.1 only: reachable from that machine, nowhere else.
  • Exposing it takes an explicit WEB_HOST=0.0.0.0 in .env. Only do that behind something that authenticates — Tailscale, an SSH tunnel, or an authenticating reverse proxy.
  • Alerts and approval requests are only visible while the dashboard is open; there is nothing to push them to.
Local mode logs in as ADMIN_USER_ID — the same id your servers, chat defaults, preferences and agent memory are already keyed to. An existing Telegram install that switches to local keeps all of it.

Local + Telegram alerts

The same install, with a bot attached. You control Condor with commands (/portfolio, /trade, /agent), agents push alerts and approval requests to your phone, and /web hands you a time-limited link to the browser dashboard. This is the difference that matters: an agent asking permission to place a trade reaches you when you are not at the machine. In Local mode that request waits until you next open the dashboard. Needs a bot token from @BotFather and your numeric Telegram user id from @userinfobot — both covered in Create a Telegram Bot below.

Server

Condor and Hummingbot API on a VPS, running whether or not your laptop is on. Everything from the path above, plus:
  • Tailscale is not optional here. Hummingbot API binds 127.0.0.1 by default, so on a VPS your laptop cannot reach it at all — and the alternative, widening API_BIND to a public interface, is the thing you do not want: Docker’s published ports are not blocked by ufw, whose rules are evaluated after Docker’s own. Tailscale gives your devices a private path without opening anything. See Do I Need Tailscale? for why.
  • The dashboard binds 127.0.0.1 and is published to your tailnet through tailscale serve — see The dashboard under Tailscale.
  • Either mode works. Telegram is the usual choice, since it reaches you off the tailnet; Local mode on a server is only reachable through the tailnet.
  • Run make doctor on the VPS after setup. It is the fastest way to catch a public bind before it matters.
The Telegram/Local distinction is recorded as CONDOR_MODE in .env (telegram or local) and is never inferred from whether a token happens to be present. To switch later, re-run make setup and pick the other mode — your TELEGRAM_TOKEN is left in place, so switching back costs nothing. “Server” is not a mode; it is where you run one of the two. For the usual sequence — Local first, Telegram later, a server after that — see Start local, grow later.

Start local, grow later

The three paths are not a one-time choice. The common route is to start with Local, attach Telegram once you want alerts reaching your phone, and involve a server once real capital is on the line. Each hop keeps what you have built — here is what each one takes.

Add Telegram to a Local install

  1. Create a bot and get your numeric user id — Create a Telegram Bot and Get Your User ID below.
  2. Run make setup. It shows the current mode and asks Keep local mode? — answer n and pick Telegram, then enter the token and your id. Everything else is remembered.
  3. make restart. Send /start to your bot.
A Local install that never had Telegram runs as user id 1. Adding a bot switches your login to your Telegram id: the API servers you configured are shared and carry over, but dashboard preferences and agent conversations are keyed per user and stay behind under id 1. Make this hop early, before there is much history to leave. (The reverse switch, Telegram → Local, keeps your id — and with it everything.)

Keep Condor local, put the API on a server

You do not have to move Condor to get 24/7 execution. Bots and executors run on the Hummingbot API server — a laptop Condor driving a VPS API keeps strategies running while the laptop sleeps. Only agents, routines and alerts live in Condor itself.
  1. On the VPS, run the Hummingbot API only installer and answer y to Tailscale — see On the API server.
  2. On your machine, install Tailscale and sign in to the same tailnet.
  3. Add the server — Settings → Servers in the dashboard (Local mode) or /servers in Telegram: host hummingbot-api, port 8000, the credentials you set on the VPS.
  4. make doctor — the new server should report reachable, authenticated.

Move Condor itself to the server

When you want agents and routines running around the clock too:
  1. Install Condor on the VPS (Quick Install), in the same mode you ran locally, and answer y to Tailscale — on a VPS it is not optional.
  2. Stop the new install (make stop) and copy your state over. From the old Condor directory:
    Custom agents in agents/ and routines in routines/ are plain files — copy those too if you wrote any.
  3. On the VPS: make doctor, then make run. If the API now lives on this same VPS, point the server entry at localhost in Settings → Servers (or /servers).
  4. Retire the old install (make stop on the laptop) so two Condors are not driving the same accounts.

Create a Telegram Bot

Telegram mode only—skip this and the next section if you chose Local mode.
  1. Open @BotFather in Telegram
  2. Send /newbot and follow the prompts to name your bot
  3. Copy the bot token (format: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz)

Get Your User ID

  1. Open @userinfobot in Telegram
  2. Send /start to receive your user ID

Secure Remote Access with Tailscale

Use Tailscale when Condor and Hummingbot API run on different machines—for example Condor on your laptop and the API on a cloud VPS.

On the API server

  1. Run the Hummingbot API only installer (or Quick Start with API enabled)
  2. When asked Use Tailscale for secure private networking?, answer y
  3. Paste your auth key and deploy:

On the Condor machine

  1. Install Tailscale and sign in to the same account
  2. In Telegram, open /servers — or in Local mode, Settings → Servers in the dashboard — and add the API with:
    • Host: hummingbot-api (MagicDNS name, not a public IP)
    • Port: 8000
    • Username / Password: same as the API .env
Test from the Condor host:

The dashboard under Tailscale

Turning Tailscale on changes where the web dashboard listens. With USE_TAILSCALE=true in .env, Condor binds the dashboard to 127.0.0.1 and publishes it to your tailnet through tailscale serve, rather than binding 0.0.0.0 and trusting the network. The port is never on a public interface, and only devices signed in to your tailnet can reach it. If the tailscale serve proxy cannot be confirmed, Condor logs an error and leaves the dashboard on loopback. It does not fall back to a public bind — a silent fallback is how a trading dashboard ends up on the open internet. So a dashboard you cannot reach from another device means the proxy is not up, not that the bind is wrong:
Do not set WEB_HOST=0.0.0.0 to “fix” an unreachable dashboard while Tailscale is on. That publishes a login-free interface in Local mode, and a login-link one in Telegram mode, to every network the machine is attached to. make doctor reports it as a failure for exactly this reason.
Tailscale also works when Condor and the API run on the same machine—you still get a stable hostname and avoid publishing port 8000 publicly.

Verify Installation

Run make doctor first—it reports dependencies, config, the AI model, the dashboard binding, Tailscale, and Hummingbot API connectivity in one pass, and exits non-zero if anything is actually broken. Telegram mode
  1. Open your Telegram bot and send /start
  2. You should see the main menu with commands like /portfolio, /trade, /agent
Local mode
  1. Open http://localhost:8088 in a browser on the machine running Condor
  2. You are logged in already—no token, no login screen
Note: After Condor starts successfully, admins should receive a Telegram message: “Condor is online and ready.” If that does not appear within a minute or two, see Troubleshooting (for example attach to the condor tmux session after Quick Start).

Access Points

Managing Services

How you manage Condor depends on how you installed it. After Quick Start (deploy installer): Condor runs in a tmux session named condor.
From the directory that contains the condor folder, you can also upgrade with:
After Manual install: make run also starts Condor in a tmux session named condor, and the Makefile wraps the same operations:
Hummingbot API (Docker): From the hummingbot-api directory (sibling of condor when installed by the deploy script):

Keeping Condor updated

Updating is admin-only and available from both surfaces, over one shared engine: /update in Telegram and Settings → Updates in the dashboard. They are two views of the same run, so an update started in Telegram can be watched to completion in the browser and vice versa — starting a second one hands you back the one already in flight rather than queueing it.
This is what makes a Local mode install self-sufficient: the dashboard panel is the surface it has, so an install with no Telegram bot does not need one to update.

What can be updated

Two components, versioned by what actually governs them: Whether the API is rebuilt from source or pulled as a published image is not a setting — docker compose config already knows, from whether the service has a build: key.

Preflight: what is in the way

Before anything runs you get the blockers, the warnings, and the ordered plan. Blockers stop the update and say why in terms you can act on — the checkout is not a git repo, it has commits the remote does not (so it cannot be fast-forwarded), local changes would be overwritten, or the registry could not be reached. Where a blocker has a safe way out it offers one: a dirty checkout can be resolved with discard or stash without leaving the panel. Warnings never refuse — they tell you a consequence worth knowing: restarting the API container stops its executors, and Condor keeps running the code it booted with until you relaunch it.

The relaunch step

An update deliberately stops one step short of running the new code. It lands the code, syncs dependencies, rebuilds the dashboard, records that a relaunch is owed, and asks you to do it. That is not an omission. Condor is almost never the top of its own process tree — started through make run, a shell wrapper, or a supervisor — so re-execing itself races the parent into bringing a second Condor up on the same port, against the same config and the same Telegram token.
Between the update and the relaunch, every seat sees a relaunch to apply banner, because the browser is running the new bundle against the old API and would otherwise just look broken. The run is journalled to data/update_run.json at every step, so the panel picks the result back up across the restart and the next boot tells you whether the update worked.

Update notifications

Condor checks in the background and tells the admin when a component falls behind — once per update, not once per check, so deferring one does not bury the rest of your notifications. Each surface gets a next step it can actually take: a Telegram message, and a bell entry that opens the update panel for a browser-only admin. Tune it with UPDATE_CHECK_INTERVAL in .env — seconds, default 3600, and 0 disables the checks. The first runs 30 seconds after startup.
Both paths are git-based, so they work on a git checkout — which is what the Quick Start and manual installs both produce. An install unpacked some other way is reported as a blocker rather than silently skipped, and updates by whatever means it was installed.

Privacy: telemetry and sharing

Two separate things, with separate switches. Neither sends anything about your trading, your keys, or your balances.

Telemetry — anonymous counts

An admin answers once, for the whole install. Three levels: The identifier is a random UUID generated once — not derived from your hostname, username, MAC, or any token. Server names, URLs and keys are never sent; a routine you wrote yourself is reported as custom rather than by name. The consent prompt offers ping or usage and has no “off” button, so ignoring it is not read as a refusal. Turning it off is a deliberate act, in any of three places — highest precedence first:
  1. .env — CONDOR_TELEMETRY=off. The full kill switch: it overrides everything and suppresses the prompt.
  2. Settings → Privacy in the dashboard — readable by every seat, changeable only by the admin. This is also where an install running without Telegram is asked in the first place, since it has no bot to be asked through. Choosing off here is the durable form: it is stored, not read from one process’s environment.
  3. config.yml — the telemetry section directly: consent: granted|denied plus level.
To see what your install is actually doing:
Downgrading from usage to ping, or turning telemetry off, is a withdrawal, not a pause — the in-memory buffer and the outbox file are deleted, so nothing already recorded can be sent afterwards.

Sharing — whole conversations

Separate from telemetry, and off until you turn it on. Telemetry is anonymous counts an admin consents to once; a share is a transcript, and only the person who held the conversation can hand it over. Secrets are redacted before a transcript is built, and again before it leaves. In Settings → Privacy:
  • Off — nothing is shared. The share button still works if you want it. This is where every install starts.
  • Ask me — the button, one conversation at a time, with the redacted transcript in front of you before you confirm.
  • Always — conversations are shared on their own, redacted the same way, without showing you first.
Always is the only path where nobody reads the payload first, so it is deliberately narrower than the button — any one of these stops a conversation going:
  • Idle — only after 30 minutes of silence in that chat.
  • Forward-only — only conversations started after you chose Always. Your archive is never swept, and turning it off and on again starts a fresh window rather than reaching back.
  • Single-author only — a Telegram group is never taken automatically. You can consent for yourself, not for the other people in the room.
  • Excluded chats, forever — every covered conversation carries an undismissable marker with one click to leave it out, and that exclusion is permanent.
  • Rate limited — at most 3 conversations per 15-minute sweep.
The operator-level kill switch:
CONDOR_SHARING=off reaches transcripts already queued and destroys them — but a queued unshare is still delivered, because a revocation is a deletion the install already promised.

Troubleshooting

Hit a snag—Condor can’t reach the API, Telegram stays quiet, Tailscale won’t connect—or want to check whether your broker needs securing? See the dedicated Troubleshooting page, which also covers the message broker security fix.

Next Step

Adding Credentials

Connect your exchange accounts
Got Condor running? Tell us how install went, what’s confusing, and what you need next.

Help shape Condor

What’s working? What’s confusing? What do you wish Condor could do? Your answers go straight to the Hummingbot Foundation team and directly shape what we build next.