Documentation

Run it in the cloud

One command makes a project its own cloud machine: conductor host new, then answer its setup questions and watch it work.

Run Conductor in the cloud — a guide

One Ironway project, one machine in the cloud running Conductor, set up from your own computer with one command. Written for someone who can use GitHub and a terminal and has never run a server. The reference behind each step is a dedicated cloud host and deploy/opentofu/; this page is the walk through, in order, with what went wrong on the first real host and how each problem now shows up.

conductor host new widget-1

It asks for what it cannot find: which of your Ironway projects the machine works, and a Hetzner API token. That makes the machine, waits while it installs itself, and ends with the machine's setup questions in the same terminal. The rest of this page is what happens in between and what those questions are.

What you get, and why a separate machine

Conductor runs Claude sessions that write code, run tests and open pull requests, and it runs them with permission prompts turned off so nobody has to sit and click "allow". That is safe on a machine that holds one project's repository and nothing else, and it is not safe on the laptop that holds your documents. So the machine is a small cloud server, one per project, made and destroyed with one command, and it never sees your files.

on the machinewhat it isneeded for the first test
Conductorclaims work from Ironway, runs a session per piece, opens the pull requestyes
TeamClaudepools several Claude Max accounts and moves to the next when one runs lowon by default, recommended
Towerlets several sessions work on one PRD at once (a "swarm")later (--tower)
Roundhouse agentruns CI for the project on the same machinelater (--roundhouse <url>)

Before you start

  • An Ironway project with a connected GitHub repository and at least one approved PRD. Note its workspace and project slugs, the two words in its dashboard address /dashboard/t/<workspace>/projects/<project>. They are not the GitHub owner and repository name.
  • A Hetzner Cloud account and an API token: the Hetzner console, your project, Security → API tokens, Read & Write. Use a Hetzner project that holds only these machines, because a token reaches every server in its project. conductor host new looks in this order and never saves it, because agent sessions run as your own user and could read any file or unlocked keychain you could:

    1. An exported TF_VAR_hcloud_token or HCLOUD_TOKEN. Do not export either in the shell that starts conductor serve.
    2. hetzner_token_command in the config, any command that prints it.
    3. 1Password, if op is installed: see below.
    4. A prompt with the input hidden. A script with no terminal must use 1 or 2.
  • Optional: tokens in 1Password. Make a vault called Ironway, or name another with onepassword_vault in the config (none turns this off). If you are signed in to several 1Password accounts, set onepassword_account to the one holding the vault, using a user ID from op account list. Add any of these as API Credential items, with the token in the credential field:

    itemused for
    Hetznermaking and destroying the machine
    Claudethe machine's CLAUDE_CODE_OAUTH_TOKEN
    GitHubthe machine's GH_TOKEN; use a token for its own GitHub account
    GitLabthe machine's GITLAB_TOKEN, for a project on GitLab
    Ironwaythe machine's IRONWAY_API_TOKEN

    Each item can also belong to one machine: GitHub tower-1 is tried before GitHub, and the same goes for the others. Give each machine its own GitHub or GitLab item when it has its own account, and its own Hetzner item when it lives in a different Hetzner project. If only a shared GitHub item exists, the command shows which account that is and asks before using it. A forge token you paste is saved under the machine's name.

    You can skip making the items. Before it creates anything, the command checks the vault, asks on your Mac for whatever is missing, shows which GitHub account a pasted token belongs to, and offers to save each one. Enter skips a token, and the machine's setup asks for it instead. It never replaces an item that exists, and the vault must exist first (op vault create Ironway). --env replaces the last three. A token in the vault only stays out of an agent's reach if 1Password asks you to approve the read. Turn on Settings → Developer → Integrate with 1Password CLI, then check that op read from a new terminal prompts you.

  • An SSH key on your Mac. conductor host new uses the agent your ~/.ssh/config names (a 1Password IdentityAgent is found with no setup), then the default agent, then ~/.ssh/*.pub. It takes the key named id_ed25519, else the first ed25519 key, prints which, and --ssh-key names another. If you use 1Password, turn on Settings → Developer → Use the SSH agent and point ~/.ssh/config at the socket path it shows you. There is no password for the server, ever; the key is how you get in.
  • A Claude token: on your Mac, claude setup-token, and keep what it prints. It is a subscription token, not an API key.
  • OpenTofu and Conductor on your Mac: brew install opentofu, and curl -fsSL https://ironway.work/install/conductor.sh | sh.

1. Make the machine

conductor host new widget-1

widget-1 is the server's name. Add --project my-workspace/my-project to skip the question. What the command does, in order:

  1. Lists the projects your Ironway login reaches and asks which, unless --project named them. Refuses straight away if tofu is not installed, then asks for the Hetzner token if it has none.
  2. Finds your ssh key and asks Hetzner whether your project already holds it, so it is never registered twice (Hetzner refuses a second copy).
  3. Writes an OpenTofu setup for this one machine under ~/.switchyard-conductor/hosts/widget-1/ and runs tofu apply. The 8-core machine (ccx33) is billed while it exists. Hetzner repriced this line in 2026, so check the console's current price before you make one, or choose a size with --server-type.
  4. Waits while the machine installs itself, printing its install log as it goes. The machine installs the published Conductor binary (checked against its published checksum) and runs conductor host bootstrap, which adds the GitHub CLI, the agent CLIs and TeamClaude. Nothing is cloned and nothing is compiled.
  5. Opens the machine's setup questions in your terminal (step 2 below).

The machine keeps itself current. Its conductor follows the published release (--auto-update=false turns that off, and --binary implies it, because an update would replace the build you uploaded), and Ubuntu installs its own security updates.

Useful flags: --project again for a second project on the same machine, --tower, --roundhouse https://rails.switchyard.work, --teamclaude=false, --server-type, --location, --no-setup to stop before the questions, and --env FILE (step 2). conductor host new -h lists them all.

2. Answer the setup questions

The questions come from conductor host setup, running on the machine. It asks only for what it cannot find, and can be run again at any point:

  1. The Claude token you made on your Mac. Paste it.
  2. GitHub. It prints a code. Open https://github.com/login/device on your Mac and enter it; the server cannot open a browser itself.
  3. Tower, if you asked for it: built now that GitHub is signed in.
  4. Ironway. It prints a link; open it and approve the machine. It then registers every project and clones its repository.
  5. The verify command for each project, the check a build must pass before its pull request opens. When the project declares one on Ironway (its settings' verify command) setup just shows it: every lane runs Ironway's, so there is nothing to answer. conductor project commands <workspace/project> prints what Ironway declares. Only for a project that declares none does setup suggest one from the repository; for a large Go project the short form (go build ./... && go vet ./... && go test -short -timeout 30m ./...) keeps each build to minutes.
  6. TeamClaude accounts. teamclaude login once per Claude Max account; use a private browser window for each extra account. Your Claude token from question 1 is still needed: the claude program checks its own login before the pool ever sees a request.
  7. Roundhouse, if you asked for it: a code to approve on its Agents page.
  8. Doctor, which lists anything left, then start.

Fewer questions. The three tokens can be written ahead of time into a file on your Mac, one NAME=value per line, and handed over with --env:

cat > ~/widget-1.env <<'TOKENS'
CLAUDE_CODE_OAUTH_TOKEN=...      # from claude setup-token
IRONWAY_API_TOKEN=...            # optional; else the browser approval in question 4
GH_TOKEN=...                     # optional; a token for the machine's own GitHub account
TOKENS
chmod 600 ~/widget-1.env
conductor host new widget-1 --project my-workspace/my-project --env ~/widget-1.env

The file travels only over the ssh connection, into the machine's own protected settings; it is never put in the machine's definition, in OpenTofu's state or in a log. Questions 1, 2 and 4 are then answered from it and the first pass runs without you. What no file can answer is a browser approval: one teamclaude login per Claude Max account, and whatever token you left out. Those are asked next, in the same terminal.

Give the machine its own GitHub account

Do this before the setup questions if you can. With your own account, the machine's pull requests look like yours, it shares your 5,000 requests an hour, and its credential can reach everything you can.

  1. Create a GitHub account for this machine, such as conductor-widget-1.
  2. Invite it as a collaborator with Write on each repository the machine works, and accept the invitation as that account.
  3. Make a token on that account. If an organization owns the repositories, use a fine-grained token scoped to them. If a personal account owns them, use a classic token with repo and workflow: GitHub does not let a fine-grained token reach a repository as a collaborator. Set an expiry you will renew.
  4. Give it to the machine. Answer the GitHub question with GH_TOKEN in --env, or put it in 1Password as GitHub. On a machine already running:

    printf '%s' "$TOKEN" | conductor github login

    Then restart serve. It checks the token with GitHub before saving it.

conductor doctor then shows github acts as conductor-widget-1, and a github-push line per project. The full permission list is the conductor's own account.

A project on GitLab

A project on GitLab, gitlab.com or your own server, works the same way. Setup asks Ironway which forge each project is on, signs in to that instance with glab, and points git at that login. It skips the GitHub question unless a project is on GitHub or you asked for --tower, because Tower's source is on GitHub.

Give the machine its own account on that GitLab, and make a personal access token with the api scope. Put it in --env as GITLAB_TOKEN, or in the vault as an item named GitLab. Without one, setup runs glab auth login in your terminal. One token covers one GitLab server, so a machine on a second server asks again.

Before you make the machine, check two things about a self-hosted GitLab: that it accepts connections from a cloud server, and that your organization allows its code on one.

A machine that only runs CI

conductor host new conductor-ci-1 --role ci --ci-pipeline conductor

That makes a host with the Go toolchain, Node, Python and a Roundhouse agent, and no conductor service, sessions, Tower or TeamClaude. Setup asks only for GitHub and the Roundhouse approval. It runs two jobs at once, and --roundhouse-spawn changes that.

--ci-pipeline is what keeps the machine to that one pipeline's jobs. Roundhouse agents share a queue, and a step's queue: "*" reaches all of them, so an agent with no pipeline would take other projects' jobs and fail every clone its GitHub account cannot make. A conductor host with --roundhouse can use it too. The tag needs a Roundhouse server new enough to honour it, so check that first: an older server ignores it.

Give the machine its own GitHub account with Read access to that one repository, and put its token in the vault as GitHub conductor-ci-1. The Roundhouse server posts the statuses, so it needs no write access. A shared GitHub item is never used on a CI machine.

Treat everything on this machine as readable by CI. Jobs run as the same user as the agent, so a job can read that token and the machine's Roundhouse credential, and a job comes from whatever branch or pull request starts it. The account's narrow access is what limits the damage: the token reaches one repository, read-only, and the server refuses the credential any tag it was not approved with. Keep nothing else on the machine, and run only pipelines you trust equally on it. If a credential may have leaked, revoke the host on the Agents page and revoke the token on GitHub.

3. See it working

From your Mac:

conductor tunnel ops@<the address>

conductor host list prints the address. It opens the machine's dashboard at http://127.0.0.1:18095, and TeamClaude's account pool is at http://127.0.0.1:13456/teamclaude/dashboard. Ctrl-C closes it. Without the conductor command, the same thing by hand:

ssh -N -L 18095:127.0.0.1:18095 -L 13456:127.0.0.1:13456 ops@<the address>

The machine uses 18095 and 13456 on purpose, so a Conductor and TeamClaude on your own Mac (8095 and 3456) keep running alongside.

The machine also shows up on Ironway under Account → Conductors, where it can be paused and resumed. The finish line for a first test is a pull request on the project's repository opened from a conductor/<install-id>/… branch.

4. When you are done

conductor host destroy widget-1 removes the machine, and then its OpenTofu setup on your Mac. Nothing on Ironway is lost; a new machine signs in and picks up where the old one stopped.

How long it takes

The figure this section records is the time from tofu apply starting to bootstrap done in /var/log/conductor-bootstrap.log, on a ccx33 host running Conductor and TeamClaude (--teamclaude, the default; no --tower, no --roundhouse), made from PRD #1081's branch against the private conductor repository, with zero operator commands typed between the apply and conductor host setup.

measuredvalue
tofu apply → bootstrap doneTODO(prd-1081): not yet measured — a person must make the host and fill this in
operator commands between apply and setupTODO(prd-1081): expected 0, to be confirmed on the live host
host, location, dateTODO(prd-1081)

No number is written here until a live host has produced one. To measure it: run conductor host new <name> --project <ws/proj> --no-setup from this branch, note when tofu apply starts printing, and read the timestamp of the bootstrap done line (conductor host new also prints a ready line with the time since it started, which includes tofu init and is therefore a little longer). The step the binary boot removed, a Go install and a source build, took ten to fifteen minutes on the first host, which itself never reached bootstrap done unattended: its clone of the private repository failed.

Troubleshooting

Every one of these happened on a real host, and each now either cannot happen or says so plainly.

what you seewhywhat to do
HCLOUD_TOKEN is not setno token was found and there is no terminal to ask onrun it from a terminal, or set hetzner_token_command
Hetzner refusedthe token is wrong or revokedmake a new one in the Hetzner console
tofu is not on PATHOpenTofu is not installedbrew install opentofu
no ssh public key foundno key in the agent your ~/.ssh/config names, the default agent or ~/.sshssh-keygen -t ed25519, or --ssh-key path/to/key.pub
ssh asks for a passwordyour Mac did not offer the keyturn on the 1Password agent (above), or --ssh-key the key ssh offers
bootstrap NOT run: no conductor binary was installedthe download origin did not answer for the whole retry windowssh ops@<the address> sudo sh /var/lib/cloud/instance/scripts/runcmd
bootstrap NOT run: the installed conductor has no conductor host commandthe published release predates the one-command hostwait for the next release, or try your build with --binary (OpenTofu README)
no bootstrap done ... after 30m0sthe install is slow or stuckit printed the last 40 log lines; --timeout 45m waits longer, and running conductor host new again resumes
switchyard named no repositorythe project was given as GitHub owner/repothe Ironway workspace/project; setup asks again
Failed to connect to bussudo -iu gives a shell without its systemd sessionfixed in setup; by hand, export XDG_RUNTIME_DIR=/run/user/$(id -u) first
Roundhouse: "no place to enter a code"approving needs the Roundhouse admin tokenleave --roundhouse off until you have it
the swarm is refused, "does not run inside Forge"swarms need Towerexpected without --tower; single beads still build

By hand

conductor host new is three steps an operator can still take one at a time, for a provider without a module or a setup it does not cover.

  1. The machine. From a checkout, deploy/opentofu/examples/hetzner: cp terraform.tfvars.example terraform.tfvars, put your ssh key in ssh_public_keys (or its Hetzner name in existing_ssh_keys) and your project's slugs in a projects entry, then tofu init && tofu apply and tofu output hosts. Or paste scripts/systemd/cloud-init.yaml as any provider's user data, with your key and project filled in.
  2. The wait. ssh ops@<the address>, then tail -f /var/log/conductor-bootstrap.log until bootstrap done.
  3. The questions. sudo -iu conductor conductor host setup (add --yes to take every detected default, --env FILE for the tokens). Paste multi-line blocks one at a time: a command that opens a new shell or asks a question swallows the lines pasted after it.