Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Plan-only vs Remote Build

espctl can run in either of two modes. Same program, same set of features — only what they actually do is different.

Remote build is the default. When you run espctl build, it sends your project to a build server and compiles it. You only get plan-only mode when you explicitly ask for it.

Remote build mode (the default)

This is what you’ll use most of the time.

espctl sends your project to the build server, the build server compiles it (in a safe sandbox), and the finished firmware comes back to you.

What you get:

  • “Build it” actually builds it.
  • You see the live build log.
  • The compiled firmware file shows up and you can download or flash it.
  • You can open an interactive serial monitor on the build server.

Where does espctl send the build? It checks in this order:

  1. The --remote <url> flag, if you passed one.
  2. The server URL saved by espctl login (in ~/.config/espctl/credentials.json).
  3. https://esphome.cloud (the built-in default).

So if you’ve run espctl login --token <your-token> once, every espctl build after that goes to your saved server automatically.

Plan-only mode

You have to ask for it — pass the --local flag:

espctl build --local --target esp32s3

In this mode, espctl can:

  • Look at your project files and check that the settings are valid
  • Tell you what a build would do, step by step
  • Read existing build output (logs, firmware files) that you already have on disk
  • Show you what ESP-IDF versions and tools the build server would use, if it were online

In this mode, espctl cannot actually compile anything. There’s no building going on.

When this is useful:

  • You’re offline (airplane, train, conference WiFi).
  • You’re reviewing a build before running it.
  • You’re learning what the tool can do without committing to anything.

Logging in

The simplest way to set up remote builds:

espctl login --token <your-access-key>

This saves your token and the server URL to ~/.config/espctl/credentials.json. From then on, every espctl build uses those credentials.

The credential file is restricted to owner-only permissions (0600). If espctl detects insecure permissions, it warns you.

HTTPS required. espctl rejects non-HTTPS server URLs by default. For local development only, you can override this with ESPCTL_ALLOW_INSECURE=1 in your environment.


MCP server mode (for AI tools)

When espctl runs as an MCP server (espctl mcp serve), mode detection works differently — it uses environment variables instead of CLI flags:

What’s set in the environmentMode
CONTROL_BASE_URL + MCP_AUTH_SECRET both setRemote build
Either variable missingPlan-only

This is what your AI tool’s config file controls. When you edit .claude/settings.json, .cursor/mcp.json, etc., you’re setting these env vars for the MCP server process.


Switching between modes

CLI users: Just pass --local when you want plan-only, or omit it for remote. No config changes needed.

MCP server users: Edit your AI tool’s config to add or remove CONTROL_BASE_URL and MCP_AUTH_SECRET from the env block, then restart the AI tool. You can’t switch mid-session.

If you want both at once, configure two espctl entries with different names (e.g. espctl-local and espctl-remote). Most AI tools let you have several MCP services side by side.


How does espctl know which mode it’s in?

CLI (espctl build)

What you doMode
espctl build (logged in via espctl login)Remote build (uses saved server)
espctl build (not logged in)Remote build → https://esphome.cloud
espctl build --remote https://my-server.comRemote build to that URL
espctl build --localPlan-only

MCP server (espctl mcp serve)

What you setMode
CONTROL_BASE_URL + MCP_AUTH_SECRETRemote build
Either env var missingPlan-only

You can confirm the mode any time by asking your assistant to “run doctor” — the report includes whether you’re logged in and whether the build server is reachable.


See also